I need to get things clear about Objective-C memory management:
If I declare an object in the class header as ivar without #property:
#interface MyFacebooDelegate : UIViewController
{
TableViewController *tableController;
}
...
#end
and some where in the code for example in - (void)viewDidLoad I do :
tableController = [[TableViewController alloc] init];
so where is best way to release it. What if I make the instant object a property what will be the different? and how the memory management will be too
#interface MyFacebooDelegate : UIViewController
{
TableViewController *tableController;
}
...
#end
#property (nonatomic, strong) TableViewController *tableController;
What the following syntax do exactly for the object viewController:
.h
#interface AppDelegate : UIResponder <UIApplicationDelegate>
#property (strong, nonatomic) ViewController *viewController;
#end
.m
#implementation AppDelegate
#synthesize window = _window;
#synthesize viewController = _viewController;
- (void)dealloc
{
[_window release];
[_viewController release];
[super dealloc];
}
.....
#end
If I want to return an object through a method to another class, do I need to autorelease it in the method body first and then retain it in receiver side?
for example this method what exactly to do in the method body and in the receiver side too:
-(NSString *)getFriendId
{
NSArray *ar = [NSArray arrayWithObjects:#"1",#"2",#"3", nil];
return [ar objectAtIndex:0];
}
I know this a lot but I am really confused and need your help.
1) best way is in dealloc; or right before re-setting it.
2) a property does the retain/release for you. But WARNING! You keep mixing up things. You use "strong" here, which relates to ARC. If you really insist on using classic retain/release (you shouldn't) then use (nonatomic, retain) instead.
3) Your properties get deallocated on dealloc. Again, strong is wrong here.
4) Yes. Ideally you should. Another reason why ARC is awesome, it does this all for you, automatically.
tl;dr: Use ARC. Never go back. (But still learn manual memory management)
ARC is the answer for your all memory management question. Very import note on Strong and Weak property in addition to ,
iOS Strong property: So strong is the same as retain in a property declaration before ARC. For ARC projects I would use strong instead of retain, I would use assign for C primitive properties.
iOS outlets should be defined as declared properties. Outlets should generally be weak, except for those from File’s Owner to top-level objects in a nib file (or, in iOS, a storyboard scene) which should be strong. Outlets that you create will therefore typically be weak by default, because: Outlets that you create to, for example, subviews of a view controller’s view or a window controller’s window, are arbitrary references between objects that do not imply ownership.
Related
This question already has answers here:
Objective-C declared #property attributes (nonatomic, copy, strong, weak)
(4 answers)
Closed 6 years ago.
I was trying to pass a custom object to the next view controller and I encountered this error -[ClassName copyWithZone:] unrecognized selector sent to instance
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"attemptDetails"])
{
ResultsVC *vc = segue.destinationViewController;
vc.selectedEntry = selectedEntry;
}
}
#property (nonatomic, retain) ClassName *selectedEntry; //Why is it retain and not copy?
I'm still very confused with property attributes and why certain types use certain attributes, like NSString uses (nonatomic, copy) and CLLocationCoordinate2D uses (nonatomic, readonly).
Could someone explain or link a reference to me how each property attribute works? Much thanks!
There are lots of descriptions for property attributes explanation,
Reference links,
Objective-C ARC: strong vs retain and weak vs assign
https://stackoverflow.com/a/4511004/4294543
#property and retain, assign, copy, nonatomic in Objective-C
Short & simple my understanding is like,
retain : It's working on the created object, and it just increase the reference count.
Here in your case you have already model class object so not need to copy in the second vc property,you just need to retain it to second vc property.
copy : The value you assigned to property can be copied & used for other purposes too(create shallow copy of object & need when object is mutable & need to release after finish with it).
nonatomic : Thread access is faster but you can't simultaneously access & change your property.
readonly : You can't directly assign the property new value.
Even i have run your case in the my project,
#import "ViewController.h"
#import "TestViewController.h"
#import "CustomClass.h"
#interface ViewController (){
CustomClass *classT;
}
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
classT = [[CustomClass alloc]init];
classT.test = YES;
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (IBAction)btn:(id)sender {
TestViewController * vc = [self.storyboard instantiateViewControllerWithIdentifier:#"TestViewController"];
vc.className = classT;
[self presentViewController:vc animated:YES completion:nil];
}
#end
#import <UIKit/UIKit.h>
#import "CustomClass.h"
#interface TestViewController : UIViewController
#property (nonatomic,retain) CustomClass *className; // Work as i said
//#property (nonatomic,copy) CustomClass *className; // Makes a copy of an object, and returns it with retain count of 1. If you copy an object, you own the copy. This applies to any method that contains the word copy where “copy” refers to the object being returned thats why here you will get crash
#end
I have read couple of good article for memory management. According to rypress
Retain Attribute : The retain attribute is the Manual Retain Release version of strong, and it has the exact same effect: claiming ownership of assigned values. You shouldn’t use this in an Automatic Reference Counted environment.
Copy Attribute : The copy attribute is an alternative to strong. Instead of taking ownership of the existing object, it creates a copy of whatever you assign to the property, then takes ownership of that. Only objects that conform to the NSCopying protocol can use this attribute.
Even I went through some good link of stackoverflow as well. Joshua Nozzi's answer gave good explanation for retain vs copy.
Retain vs. Copy - Declared properties use retain by default (so you can simply omit it altogether) and will manage the object's reference count automatically whether another object is assigned to the property or it's set to nil; Use copy to automatically send the newly-assigned object a -copy message (which will create a copy of the passed object and assign that copy to the property instead - useful (even required) in some situations where the assigned object might be modified after being set as a property of some other object (which would mean that modification/mutation would apply to the property as well).
Also found good example here.
Code :
NSMutableArray *array = [[NSMutableArray alloc] initWithObjects:#"First",#"Second", nil];
NSMutableArray *copiedArray = [array mutableCopy];
NSMutableArray *retainedArray = [array retain];
[retainedArray addObject:#"Retained Third"];
[copiedArray addObject:#"Copied Third"];
NSLog(#"array = %#",array);
NSLog(#"Retained Array = %#",retainedArray);
NSLog(#"Copied Array = %#",copiedArray);
Output :
array = (
First,
Second,
"Retained Third"
)
2013-12-19 17:15:49.380 RetainVsCopy[2876:c07] Retained Array = (
First,
Second,
"Retained Third"
)
2013-12-19 17:15:49.381 RetainVsCopy[2876:c07] Copied Array = (
First,
Second,
"Copied Third"
)
See, both array and Retained Array are having same contents. This is because both are pointing to same memory/instance/object. Where as contents of Copied Array are different. This is because copy created a separate instance.
In Objective C you will find that each class actually has a structure behind it. The properties are shortcuts which create the value in structure, a getter and a setter. For instance:
#interface MyClass
#property id myValue;
#end
Will create:
#interface MyClass {
id _myValue;
}
#property id myValue;
#end
#implementation
- (id)myValue {
return _myValue;
}
- (void)setMyValue:(id)myValue {
_myValue = myValue;
}
#end
Now these flags such as retain and copy add additional logic to the setters and getters. Using copy will actually create a setter as:
- (void)setMyValue:(id)myValue {
_myValue = [myValue copy];
}
Which means that the value must have the copy method implemented. Since your object does not it crashes.
Why to use copy is for safety. This is rarely important for something as strings but it is important for something like an array. So for instance you create a property #property NSArray *myArray; which expects an un-mutable array but the problem is that you can set a mutable array as well: myClassInstance.myArray = [[NSMutableArray alloc] init];. Now 2 modules have the access to the same mutable array. So if the first object starts modifying the array while the other one expects the array to always be the same you may find some issues. For instance MyClass instance may use it as a data source for the table view and at some point the array is mutated but the cells are not added/removed and the table view will cause a crash.
To be honest you can simply leave all of these as default and modify them only when you really need to. The case like above is highly unlikely anyway.
I am developing an ARC enabled project. From a view controller I am pushing MyClass,
- (void)pushMyClass {
MyClass *myClass = [[MyClass alloc] init];
[self.navigationController pushViewController:myClass animated:YES];
}
After doing some operations I am popping MyClass. The problem here is that MyClass is not getting deallocated. Following is how the classes look.
/* MyHelperClassDelegate */
#protocol MyHelperClassDelegate <NSObject>
- (void)helperDidFinishHelping:(MyHelperClass *)helper;
#end
/* MyHelperClass Interface */
#interface MyHelperClass : NSObject {
__weak id <MyHelperDelegate> delegate;
}
#property(nonatomic, weak) id<MyHelperDelegate> delegate;
- (void)startHelping;
#end
/* MyHelperClass Implementation */
#implementation MyHelperClass
#synthesize delegate;
- (void)dealloc {
delegate = nil;
}
/* MyClass */
#interface MyClass : UIViewController <MyHelperClassDelegate> {
MyHelperClass *helper;
}
#implementation MyClass {
- (void)dealloc {
helper.delegate = nil;
}
- (void)getHelp {
helper = [MyHelperClass new];
helper.delegate = self;
[helper startHelping];
}
- (void)helperDidFinishHelping:(MyHelperClass *)helper {
}
}
MyHelperClass calls a web service using NSMutalbleURLRequest & NSURLConnection to fetch some data and saves it to user defaults.
One thing to notice here is, if I comment the line helper.delegate = self;, then MyClass gets deallocated.
What to do to make MyClass get deallocated when it is popped out of navigation controller?
Thanks.
Your delegate code looks correct (except your use of an ivar, you don't show a #synthesize so you may have _delegate and delegate both). Its quite likely that something else is retaining MyClass. What I suggest you do is add a NSLog to your MyClass dealloc. Then push it, and immediately hit the back button and see if its dealloc'd or not. If not, then take a hard look at what you do in viewDidLoad et al and start commenting out sections of that code until you can get the dealloc.
Also, I assume you don't keep a strong reference in the class that pushes the MyClass object.
I agree with Chuck that one cannot say much from the code provided. But one reason why the MyClass object is not deallocated might be that it is retained by your helper object since delegate is declared as strong, and the MyClass object has the property helper also declared as strong. In this case you had a retain cycle, and none of them can be released.
The trick could possibly lie within the fact that you use NSURLConnection. It is not specified how you use this class with the code that you've provided, but please note the special considerations referenced in the NSURLConnection class reference:
Special Considerations: During the download the connection maintains a
strong reference to the delegate. It releases that strong reference
when the connection finishes loading, fails, or is canceled.
Let's consider an application with highly customized or complex views.
We'll have a specific kind of view-controller sending methods to a specific kind of UIView, where the UIView is itself composed of a number of other views.
The view should have a rich, domain-specific interface, allowing the controller to act is a thin "glue" layer between it and a similarly rich model.
So we override our controller's view property as follows:
#interface PlaybackViewController : UIViewController<StageLayoutDelegate, ControlPanelDelegate>
{
NSMutableArray* _sections;
LightingMode _lightingMode;
}
#property (nonatomic, strong) PlaybackView* view; // <------ Specific type of view
#pragma mark - injected
#property (nonatomic, strong) id<OscClient> oscClient;
#property (nonatomic, strong) AbstractStageLayoutView* stageLayoutView;
#end
Ovverriding makes sense over defining another accessor, and I can just send messages to the specific type of UIView without having to cast.
Problem: The only problem is that it results in a compiler warning:
property type 'PlaybackView *' is incompatible with type 'UIView *' inherited from 'UIViewController'
. . and I like to build code that doesn't have any warnings. This way a valid warning doesn't get missed by being buried amongst other warnings.
Question:
Is there a way to suppress this particular warning?
Why is this part of the default settings, when most modern OO languages will happily allow overriding a property or method in a sub-class so that it returns a more specific sub-class of the type declared in the super-class?
The problem here is not not the override of the property, its using a forward declaration of the class type.
So this...
#class PlaybackView;
#interface PlaybackViewController : UIViewController
#property (nonatomic, strong) PlaybackView* view;
#end
will give you the mentioned warning because the compiler cannot know the inheritance hierarchy of PlaybackView. UIViewController has a contract to provide a UIView from its view property
Its telling you that it thinks PlaybackView is not a UIView
The simple solution here is to use a #import instead to give the compiler full knowledge of PlaybackView...
#import "PlaybackView.h"
#interface PlaybackViewController : UIViewController
#property (nonatomic, strong) PlaybackView* view;
#end
alternatively (but really bad form as the PCH is an optimising feature and shouldn't manage dependancies ) is to add #import "PlaybackView.h" to your projects PCH
As suggested in another answer using #import instead of #class will clear the warning but it is advised to import as little as possible in the header, so I would recommend leaving the view unchanged and having an additional PlaybackView * playbackView:
It is perfectly fine to have both view and playbackView pointing to the same view.
Classes that need to have knowledge of your specialized view have to import your controllers header, so they could just use playbackView in the first place.
More important, if you want to embed your specialized view as a subview in the future (which happens often like adding a UIScrollView superview), you won't have to refactor other code and classes!
It's plain cleaner.
I do not think override UIViewControllers view property is a good way .
I think it is better to do like this :
#interface PlaybackViewController : UIViewController<StageLayoutDelegate, ControlPanelDelegate>
{
NSMutableArray* _sections;
LightingMode _lightingMode;
}
//#property (nonatomic, strong) PlaybackView* view; //you do not need this property
#pragma mark - injected
#property (nonatomic, strong) id<OscClient> oscClient;
#property (nonatomic, strong) AbstractStageLayoutView* stageLayoutView;
#end
and in the .m file .
- (void)loadView
{
PlaybackView *mainView = [[PlaybackView alloc] initWithFrame:[UIScreen mainScreen].applicationFrame];
// set the mainView
self.view = mainView;
}
and you can use your PlaybackView like this .
((PlaybackView *)(self.view)).oscClient
or
((PlaybackView *)(xxxViewControler.view)).oscClient
Perhaps you could declare another method that provides the cast for you, in a sense.
#implementation PlaybackViewController
- (void)viewDidLoad {
[super viewDidLoad];
// use view_ property instead of view
self.view_.foo = 1;
}
- (void)loadView {
CGRect frame = [UIScreen mainScreen].applicationFrame;
self.view = [[PlaybackView alloc] initWithFrame:frame];
}
- (PlaybackView *)view_ {
return (PlaybackView *)self.view;
}
Not exactly the cleanest approach, but it does avoid the cast on self.view (by not using self.view, though)
[UPDATE]
Finally I've probably found a solution that suit the problem:
This is a quick and dirty just to suppress the warning, try to wrap your code between these lines
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wgnu"
//YOUR CODE
#pragma clang diagnostic pop
or -Wall
To see more about compiler warning suppression Clang Manual
[OLD ANSWER]
I'd like to give my 2 cents.
If I understood well you are trying to create a sort of Abstract factory, that gives you a specialized version of a view based on the view controller funcionality. In my opinion storyboards doesn't work well in that kind of design, but I'd like to give you my vision about it.
First I will create an abstract class for your view controller where in the interface you declare all property you need in all your VC sublcasses, such as :
OSClient
AbstractStageLayoutView
PlaybackView as weak
playbackProperty
The PlaybackView class is a class cluster such as NSNumber, you call a factory method on it, that will return an object that could be different from case to case. If you inspect an NSnumber it returns a different object if you create a float or an integer, but they are all subclasses of NSNumber, and NSNumber declares all the property of its subclasses, but it doesn't implement them.
Now what you can do in the -viewDidLoad method of the abstract class is call a method like that
PlaybackView *plbackView = [PlaybackView playbackViewFroProperty:self.playbackProperty];
[self.view addSubview:playbackView];
self.playbackView = plbackView;
The playbackProperty can be valued inside the User defined runtime attibute in the viewcontroller storyboard editor.
I'm trying to set the delegate for my custom protocol that has one required method allowing me to pass an array of objects back in the hierarchy of two UITableViewControllers. My delegate continues to return nil. Due to this, my required method is never called.
I'm wondering if the datasource and delegate implementations with my UITableViewControllers is causing a conflict. Also, perhaps ARC is getting in the way when declaring the delegate?
It should be noted that both UITableViewControllers were built using Storyboard and are navigated using segues within a UINavigationController (not sure if this may be causing issues or not).
The nav is --> AlarmViewController --> AlarmDetailsViewController. I create an Alarm object in my AlarmDetailsViewController that contains all the details for an alarm, place it into an array and I want to pass that array back to my AlarmViewController to be displayed in a custom cell in the table.
NOTE: I want to use the Delegate pattern here. I'm not interested in solutions that invoke NSNotifications or use my AppDelegate class.
AlarmDetailsViewController.h
#import "Alarm.h"
#protocol PassAlarmArray <NSObject>
#required
-(void) passAlarmsArray:(NSMutableArray *)theAlarmsArray;
#end
#interface AlarmDetailsViewController : UITableViewController <UITableViewDataSource, UITableViewDelegate>
{
//.....
id <PassAlarmArray> passAlarmsArrayDelegate;
}
#property (nonatomic, retain) id <PassAlarmArray> passAlarmsArrayDelegate;
#end
AlarmDetailsViewController.m
#import "AlarmDetailsViewController.h"
#interface AlarmDetailsViewController ()
#end
#implementation AlarmDetailsViewController
#synthesize passAlarmsArrayDelegate;
-(void) viewWillDisappear:(BOOL)animated
{
NSLog(#"delegate = %#", self.passAlarmsArrayDelegate); // This prints nil
[[self passAlarmsArrayDelegate] passAlarmsArray:alarmsArray];
}
//....
#end
AlarmViewController.h
#interface AlarmViewController : UITableViewController <UITableViewDataSource, UITableViewDelegate, PassAlarmArray>
{
//...
AlarmDetailsViewController *alarmDetailsViewController;
}
#property (nonatomic, retain) AlarmDetailsViewController *alarmDetailsViewController;
#end
AlarmViewController.m
#import "AlarmViewController.h"
#import "AlarmDetailsViewController.h"
#import "AlarmTableViewCell.h"
#import "Alarm.h"
#interface AlarmViewController ()
#end
#implementation AlarmViewController
#synthesize alarmDetailsViewController;
- (void)viewDidLoad
{
[super viewDidLoad];
// This is where I'm attempting to set the delegate
alarmDetailsViewController = [[AlarmDetailsViewController alloc]init];
[alarmDetailsViewController setPassAlarmsArrayDelegate:self];
}
//....
//My #required protocol method which never gets called since my delegate is nil
-(void) passAlarmsArray:(NSMutableArray *)theAlarmsArray
{
alarmsTableArray = theAlarmsArray;
NSLog(#"alarmsTableArray contains: %#", alarmsTableArray); // Never gets called due to delegate being nil
NSLog(#"theAlarmsArray contains: %#", theAlarmsArray); // Never gets called due to delegate being nil
}
#end
I've attempted to set the delegate in a method that fires when a button is pressed in AlarmViewController (as opposed to the viewDidLoad method) but that does not work either.
I'm assuming I've got a logic flow error somewhere here . . . but nearly 2 days of hunting and rebuilds haven't uncovered it. Ugh.
You're setting your delegate in the wrong place, and on a different instance of the controller than the one you will get when you do the segue. You should set the delegate in the prepareForSegue method if you're pushing AlarmDetailsViewController from AlarmViewController
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
AlarmDetailsViewController *alarm = segue.destinationViewController;
alarm.passAlarmsArrayDelegate = self;
}
You really need to understand the life cycle of view controllers, how and when they're instantiated, and when they go away. This is the very heart of iOS programming, and Apple has extensive documentation on it. Reading up on segues would also be very useful. A segue (other then an unwind segue) always instantiates a new instance of the destination controller. So, when your segue is performed, whether directly from a button, or in code, a new (different from the one you alloc init'd directly) details controller is instantiated. Before that segue is performed, prepareForSegue: is called, and that's when you have access to the one about to be created. That's the place to set a delegate or pass any information on to the destination view controller.
Did you try replace (nonatomic, retain) with (nonatomic, strong) since you are using ARC?
Auto-synthesized properties like your alarmDetailsViewController property have backing ivars prefixed with underscores, e.g. _alarmDetailsViewController. Your alarmDetailsViewController ivar (the alarmDetailsViewController declared inside the #interface ... {} block in AlarmViewController.h) is different from the backing ivar of your alarmDetailsViewController property.
Just delete your alarmDetailsViewController ivar and use the #property, preferably through self.alarmDetailsViewController.
The Problem
An IBOutlet is released before I have a chance to use it.
What I Want
I want to access a navigation controller from my app delegate so I can reload a table view.
My Setup
I have:
A Main.xib that's set as my main interface in target settings
An IBOutlet to the navigation controller as an ivar on my app delegate
This IBOutlet hooked up to the correct navigation controller in Main.xib
App Delegate is instantiated in the xib but not set as File's Owner
I'm using ARC, Xcode 4.3.2 and iOS5.1
What I've Tried
Changing deployment target
Putting a break point on dealloc for the navigation controller, app delegate - they're never called
Reading everything I can find on ARC and IBOutlets - nothing seems to contradict what I'm doing
Creating a fresh project with just a the minimum classes required - I see exactly the same problem
Code
KPAppDelegate.h
#interface KPAppDelegate : UIResponder <UIApplicationDelegate> {
IBOutlet KPBrowseExpensesNavigationController *nc;
}
#property (strong) IBOutlet KPBrowseExpensesNavigationController *nc;
KPAppDelegate.m
#implementation KPAppDelegate
#synthesize nc;
-(void)setNc:(KPBrowseExpensesNavigationController *)nc_ {
nc = nc_; // This gets called on view load and nc gets set.
}
...snip...
// This is called about 5 seconds after app startup
-(void)objectLoader:(RKObjectLoader *)objectLoader didLoadObjects:(NSArray *)objects {
// By the time we get here, nc is nil.
UITableViewController *tvc = [[nc viewControllers] objectAtIndex:0];
[[tvc tableView] reloadData];
}
#end
UPDATE
I must be doing something really silly here. Even an incredibly simple project still shows this problem. See link below.
Download a simple test project that shows the problem.
In Window nib, set the FilesOwner Class as UIApplication and then point it's delegate from Outlets to the AppDelegate object. This is what is wrong in your project example.
is your outlet from the Interface Builder set as an KPBrowseExpensesNavigationController type?
If not it is not going to create the connection between your nib and ViewController.
You should set its Custom Class as KPBrowseExpensesNavigationController in the Identity Inspector
I am not sure why you declare it as a property & a non-property. I should do something like this:
#interface KPAppDelegate : UIResponder <UIApplicationDelegate>
#property (nonatomic, strong) IBOutlet KPBrowseExpensesNavigationController *nc;
And in your implementation:
#implementation KPAppDelegate
#synthesize nc = _nc; // So you don't accidentally use nc
...snip...
// This is called about 5 seconds after app startup
-(void)objectLoader:(RKObjectLoader *)objectLoader didLoadObjects:(NSArray *)objects {
// By the time we get here, nc is nil.
UITableViewController *tvc = [[**self.nc** viewControllers] objectAtIndex:0];
[[tvc tableView] reloadData];
}
#end
Hope this helps!
I didn't see where you alloc your nav controller. Just declaring the property won't assign any value to it, so it would be nil. In you -didFinishLaunchingWithOptions in the app delegate, set your alloc/init statement. Everything else looks fine.
KPBrowseExpensesNavigationController *nc = [[KPBrowseExpensesNavigationController alloc] init];
If you have a custom init, you can use that too, but just make sure to set it up before you try and use it.