I am defining a NSString in a header file, but it is passing null when I try to log that string from that controller, but logs the value of the variable when if it is not called from the view controller, which is a problem because it also passes null when I try to log it into a different view controller.
I have found other similar questions, but no solutions seem to be helping, so if you have a solution that would be fantastic.
I have defined it as such in the header file:
#import <UIKit/UIKit.h>
#interface QuestionController : UIViewController
#property(weak, nonatomic) NSString *question;
#end
I have called it as such in the main file:
#import "QuestionController.h"
#implementation QuestionController
#synthesize question;
-(void) viewDidLoad{
[super viewDidLoad];
question = [NSString stringWithFormat:#"hey"];
NSLog(#"%#", question);
QuestionController *questionController = [[QuestionController alloc]init];
NSLog(#"%#", questionController.question);
}
#end
When you write
QuestionController *questionController = [[QuestionController alloc]init];
NSLog(#"%#", questionController.question);
you're not "logging that string from that [current] controller." Instead you've created an entirely separate instance of your QuestionController (not the same as the current view controller from which you just set question) and since you haven't set its question property at any point, that NSLog will print nil.
In your first NSLog however, i.e.
question = [NSString stringWithFormat:#"hey"];
NSLog(#"%#", question);
you are in fact logging question as set in the current instance of your QuestionController so it prints as expected.
To set questionController's question property to contain question though, try:
QuestionController *questionController = [[QuestionController alloc]init];
questionController.question = question;
NSLog(#"%#", questionController.question);
I have cleaned up your code to make it work and at the bottom you will find an explanation.
#import <UIKit/UIKit.h>
#interface QuestionController : UIViewController
#property(strong, nonatomic) NSString *question;
#end
#import "QuestionController.h"
#implementation QuestionController
-(void) viewDidLoad{
[super viewDidLoad];
_question = #"hey";
NSLog(#"%#", _question);
QuestionController *questionController = [[QuestionController alloc]init];
questionController.question = _question;
NSLog(#"%#", questionController.question);
}
#end
There are a few things to take note of.
NSString *question is now a strong reference, not weak. If you leave it as weak, things such as question = #"Hello" will not work because there is not strong reference to that string object and it will be deallocated.
I removed #synthesize, you no longer need to do this unless you are using a much older version of Xcode
I changed question to _question, you are now accessing the ivar directly, or you can user self.question in that instance to access it through the getter / setter.
removed the stringWithFormat part, you were not really using that method and doing just #"what you want to write" will suffice.
You are actually allocating a new version of QuestionController when you do QuestionController *questionController = [[QuestionController alloc]init]; This means that you now have a QuestionController which creates another QuestionController when it loads the view.
questionController.question = _question; sets the question string object on the new QuestionController object that you create when the view of the first QuestionController loads to the question string that you have in the original QuestionController.
Hope this helps you to understand exactly what was going on in your code. I am assuming that you are not intentionally trying to create a new QuestionController when the first one loads. This would also cause some sort of endless loop since every one you create will create another one when you access its view property.
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 have a situation where I have copied a string in an instance of a helper class, retained it, and later released it during the dealloc of the view controller instance that alloc'd the helper class. This results in the dreaded EXC_BAD_ACCESS. I then went to Instruments to debug zombies. This gave me the following error:
An Objective-C message was sent to a deallocated 'CFString (immutable)' object (zombie) at address: blah blah
When I then look at the allocation summary within Instruments and work backwards from the zombie detection, the first time my code is listed is in the deallocation of the helper class instance. Here is what the helper class looks like. First the .h file:
#interface channelButtonTitles : NSObject {
NSString *channelTitle;
...
}
#property (nonatomic,copy) NSString *channelTitle;
...
#end
Then the .m file:
#implementation channelButtonTitles
#synthesize channelTitle;
...
- (void)dealloc {
[channelTitle release];
...
}
#end
Now relevant code from the view controller that uses the helper class looks like the following. In the .h file I have an array that will hold multiple objects of the helper class as follows:
#interface MyVC : UIViewController {
NSMutableArray *channelTitles;
...
}
#property (retain, nonatomic) NSMutableArray *channelTitles;
Then in the .m code, I synthesize channelTitles. I also have a dealloc method as follows:
- (void)dealloc {
[channelTitles release];
...
}
Finally, I alloc object instances of the helper class and store them in channelTitles with strings stored in the channelTitle elements of channelButtonTitles as follows:
[channelTitles removeAllObjects];
self.channelTitles = nil;
channelTitles = [[NSMutableArray alloc] init];
...
for (int i=0; i<numberOfTitles; i++) {
// For each mediaItem, get the title and subtitle info
channelButtonTitles *aChannelButtonTitle = [[channelButtonTitles alloc] init]; // create an object to hold the title and miscellaneous data
aChannelButtonTitle.channelTitle = #"some title";
[channelTitles addObject: aChannelButtonTitle]; // add the title
[aChannelButtonTitle release];
}
So, this is a technique I have used many times before, but seems to not be happy now. When the view controller is popped and I return to the root view controller, the dealloc method in my view controller is called. That releases channelTitles which results in calling dealloc on the channelButtonTitles helper class objects that are stored in channelTitles.
Since I have used copy in the property of my helper class, I assume I own this string. Hence, I am releasing it. If I comment out the [channelTitle release] line from my dealloc, the EXC_BAD_ACCESS goes away, but I suspect I have a memory leak now. Please help me see what I am doing wrong.
I have a problem understanding the Objective-C and the ARC.
As I understood the strong pointers will be dealloced automatically for you, so you don't have to think about it (dealloced in dealloc method, or after the last time the object was used ?).
So I wrote a little app, with 2 viewControllers and a NavigationController, which enters one view and then goes back.
The dealloc method was called, but the property, which I set at viewDidLoad method, wasn't deallocated, it is still pointing to some object.
Code:
The first viewController has a button, which by pressing it, performs a segue to another viewController. No code there.
SecondViewController.m
#interface SecondViewController ()
#property (nonatomic, strong) NSString *myString;
#end
#implementation SecondViewController
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(#"%#", _myString);
_myString = #"it works";
}
- (void)dealloc {
NSLog(#"%#", _myString);
// and now it is deallocating the _myString property ???
}
#end
Then, I tried to do another thing.
The idea was to create a weak pointer, which points to the same memory address, as the strong pointer. I though, that the weak pointer should be nil in any case.
Since the dealloc method is called, all weak pointers should be niled
Since the strong pointer was used only in viewDidLoad, it should be deallocated way before the dealloc method.
The problem is, it is not deallocated.
Why ?
Code for secondViewController:
#interface SecondViewController ()
#property (nonatomic, strong) NSString *myString;
#property (nonatomic, weak) NSString *test;
#end
#implementation SecondViewController
- (void)viewDidLoad
{
[super viewDidLoad];
NSLog(#"%#", _myString);
_myString = #"it works";
_test = _myString;
}
- (void)dealloc
{
NSLog(#"%#", _test);
}
#end
Deallocation of the properties happens at the end of the dealloc method. If you overwrite the dealloc method, the properties won't yet be deallocated inside that method.
You could test this by creating a weak property in your first view controller, assign the strong property of the second view controller, then log the value of it when the application returns to the first view controller.
The simplest way to illustrate weak references is with the following example...
Given the following two classes:
#interface ObjectWithStrongRef : NSObject
#property (strong) NSString *ref;
#end
#interface ObjectWithWeakRef : NSObject
#property (weak) NSString *ref;
#end
We will create an instance of ObjectWithWeakRef with a scope larger than that of ObjectWithStrongRef, assign the latter's ref property a value, then have the former's ref point to this same object, then we will check ref in both scopes.
int main(int argc, const char * argv[]) {
ObjectWithWeakRef *weak = [[ObjectWithWeakRef alloc] init];
#autoreleasepool {
ObjectWithStrongRef *strong = [[ObjectWithStrongRef alloc] init];
strong.ref = [NSString stringWithFormat:#"Hello %#", #"World"];
weak.ref = strong.ref;
NSLog(#"Weak.Ref = %#", weak.ref);
}
NSLog(#"Weak.Ref = %#", weak.ref);
}
Note, that we can't simply assign ref to a literal string. Objective-C tends to keep these around in memory so it can do some memory optimizations, but when we use stringWithFormat:, it'll create an autoreleasing string.
At the first NSLog statement, strong.ref maintains a strong reference to the string object, so when we log weak.ref, the object is not yet deallocated, so it correctly logs "Hello World".
Between the first and second NSLog call, we've exited the #autoreleasepool, within which the strong object was scoped (if we put an NSLog message in ObjectWithStrongRef's dealloc, we'd see it called here). Because strong has deallocated as we exit the #autoreleasepool, there are no longer any strong references to the string object we have references to--we only have weak's weak reference to the memory, so the string object also deallocates (just after strong has deallocated).
So in the second NSLog call, we'll see Weak.Ref = (null) printed.
I have a main ViewController that contains a desginated class. Within that ViewController there is a Container that is linked to an embed ViewController. Within that embed ViewController I am creating an NSMutableArray. I am not trying to access that array inside the main ViewController. I know that if I use:
create_challenge_peopleSelect *myScript = [[create_challenge_peopleSelect alloc] init];
NSLog(#"%#",myScript.selectedCells);
The NSLog will output null because I am creating a new ViewController and that gets rid of the already set array. So my question is how can I access that array without overwriting it?
UPDATE:
Heres where the NSMutableArray is being created:
create_challenge_peopleSelect.h:
#property (strong, nonatomic) NSMutableArray *selectedCells;
create_challenge_peopleSelect.m:
if([selectedCells containsObject:label.text])
{
cell.accessoryType = UITableViewCellAccessoryNone;
[selectedCells removeObjectIdenticalTo:label.text];
}
else
{
cell.accessoryType = UITableViewCellAccessoryCheckmark;
[selectedCells addObject:label.text];
}
This class is the container class off the main ViewController
No I want to access the selectedCells within my main ViewController, I have been doing things such as:
create_challenge_peopleSelect *myScript = [[create_challenge_peopleSelect alloc] init];
I would prefer to stay away from the App Delegate If possible.
You seem to be unclear on the difference between classes and instances. OK, so, say we have two NSArrays:
NSArray *a = [[NSArray alloc] initWithObjects:#"hello", #"I", #"am", #"an", #"array", nil];
NSArray *b = [[NSArray alloc] initWithObjects:#"so", #"am", #"I", nil];
If I do a.count, I'll get 5 as the answer because the array contains five objects. Meanwhile, if I do b.count, I'll get 3, because that array contains three objects. It isn't that creating b "gets rid of the already set count". They are separate objects completely unrelated to each other.
Your view controller class is the same way. When you create a different instance, it doesn't overwrite the old one -- it's just not the same object. In order to use the original view controller object, you need to get a reference to it.
So how do you get a reference to it? Well, the general answer is you design your app so that the two objects know about each other. There are lots of specific ways to accomplish this. A lot of people will say "Just stick a reference in the app delegate." That is one thing you can do, but it's not always the best choice. It can get out of control if you just stick everything in your app delegate. Sometimes it's the right answer, often other things are the right answer. Another approach is to have an object that knows about both of those objects introduce them to each other. But sometimes there is no such object. So it's situational.
Basically, instead of creating a new view controller, you need to maintain a pointer to the original.
I suggest storing an instance of your UIViewController in the AppDelegate in order to retain the particular instance of the view controller you've created by making it a global variable.
ex. In the App Delegate.h
#import "ViewController.h"
#class ViewController;
#interface AppDelegate : UIResponder <UIApplicationDelegate>
#property (nonatomic) ViewController *viewController;
Then from whatever view controllers' .m's from which you need to read/write to the variable, create a pointer to the application's app delegate, ex:
#import "AppDelegate.h"
#interface WhateverViewController ()
AppDelegate *mainDelegate;
- (void)viewDidLoad {
mainDelegate = (AppDelegate*)[[UIApplication sharedApplication] delegate];
}
So wherever you first create that view controller in your code (before ever using it), initialize it using this global variable. ex. If you're using xibs:
mainDelegate.viewController = [[UIViewController alloc] initWithNibName:#"ViewController" bundle:nil];
[self.navigationController pushViewController:mainDelegate.viewController animated:YES];
ex. If you're using storyboards:
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"StoryboardName" bundle:nil];
mainDelegate.viewController = [storyboard instantiateViewControllerWithIdentifier:#"viewControllerID"];
[self.navigationController pushViewController:mainDelegate.viewController animated:YES];
(This is assuming it's in a place other than the app delegate in which case the pointer to the App Delegate isn't needed.)
Then when accessing the array from another UIViewController use
mainDelegate.viewController.array
To access the NSMutableArray from one class to another class use following code.
In the first view controller in which u have declared the object of NSMutableArray, declare the property and synthesize for the same as below,
//In FirstViewcontroller.h class,
#property (nonatomic, strong) NSMutableArray *arrData;
//In FirstViewcontroller.m class
#synthesize arrData;
Also FirstViewcontroller object should be global so you can create the object of FirstViewcontroller in app delegate file.
//appdelegate.h
#property (nonatomic, strong) FirstViewcontroller *objFirst;
//appdelegate.m
#synthesize objFirst;
FirstViewcontroller *objFirst=[[FirstViewcontroller alloc]init];
Now in SecondViewcontroller in which you have to access array,
create the share object of Appdelegate file
//SecondViewcontroller.m
AppDelegate *app = (AppDelegate*)[[UIApplication sharedApplication] delegate];
Then use will get the required array as below,
app.objFirst.arrData
This is your required array I hope it will help you.
The basic idea here is that in your original class, the array is referred to by a pointer. Your original class would allocate it and presumably load it. Other parts of your program can be handed the contents of the property, which is a pointer, assign that to their own pointer holder, and use it as if you had declared it there. Please use the above code;
MyClass *aClass = [[MyClass alloc] initWithMyInitStuff];
NSMutableArray *ThatArray = aClass.MyArray;
NSLog("Count of ThatArray: %d", [That.Array count]);
What you've done in the code provided is set a public property for a mutable array...
#property (strong, nonatomic) NSMutableArray *selectedCells;
The NSMutableArray is not "created" by setting that property. At some point in your code you also have to create the NSMutableArray by initialising...
NSMutableArray *selectedCells = [[NSMutableArray alloc] init];
or by using a convenience method such as...
NSMutableArray *selectedCells = [NSMutableArray arrayWithCapacity:(NSUInteger)<initialising capacity>];
or
NSMutableArray *selectedCells = [NSMutableArray arrayWithArray:(NSArray *)<initialising array>];
Initialising an NSMutableArray is often done only once. If it is repeated, the contents are overwritten against the property used to point to the array. As such, a useful location for this is often within the viewDidLoad view controller lifecycle method.
This is what I got so far.
mainview.m
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender(id)sender {
secondView *secView = [segue destinationViewController]
secView.ext = #".com";
}
secondView.h
#interface secondView : UIViewController {
NSString *ext;
}
#proper (nonatomic, retain) NSString *ext;
#end
secondView.m
-(void)viewDidLoad {
NSString *url = [#"www.google" stringByAppendingFormat:ext]
}
And its returning a error saying ext is null... what am I doing wrong?
Did you try to turn ext into a property instead? My understanding is that the "dot" notation essentially turns your code into
[secView setExt:#".com"]
So turn SecondView.h into
#interface secondView : UIViewController
#property (nonatomic, copy) NSString *ext;
#end
And don't forget to #synthesize it in your .m file
Check the followings:
Make sure #synthesize ext; is in SecondView.m
In Storyboard, have you linked the segue correctly?
In Stodyboard, is the viewController that represetns SecondView defined as a class of SecondView?
Make sure that you are calling the SecondView via prepareForSegue:sender method (i.e. SecondView doesn't get called by pushViewController:animated somewhere else in your code).
Put a breakpoint at the line:
secView.ext = #".com";
and make sure that the ext ivar is properly set after the assignment. If it's not, you might be specifying that the accessors use a different ivar in your #synthesize directive for ext, or you might have provided a setter -setExt: that doesn't properly set the ivar.
If ext is set properly after the assignment, perhaps you've got two different instances of secondView. That used to happen a lot when people used .xib files -- they'd create one view controller in code and have another instance of the same class in their .xib file. I wouldn't expect that to be the case here since you're getting secView straight from the segue, but it's worth thinking about.
There are two views: view1 and view2.
Add view2.h file in view1.h
init object of view2 and set their variable.