In http://www.raywenderlich.com/21320/objectively-speaking-a-crash-course-in-objective-c-ios6 there is a cut-and-paste XML version of a property list. I have the following code:
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
NSString *plistCatPath = [[NSBundle mainBundle] pathForResource:#"quotes" ofType:#"plist"];
self.movieQuotes = [NSMutableArray arrayWithContentsOfFile:plistCatPath];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
-(IBAction)quoteButtonTapped:(id)sender {
int array_total = [self.movieQuotes count];
int index = (arc4random() % array_total);
NSString *my_quote = self.movieQuotes[index][#"quote"];
self.quoteText.text = [NSString stringWithFormat:#"Quote:\n\n%#", my_quote];
}
The third-to-last line in quoteButtonTapped is crashing because I'm trying to take the modulus of 0. That means that self.movieQuotes is registering as empty.
quotes.plist is stored in the root of the directory, and it appears to be the same, modulo one comment and whitespacing, as in the tutorial.
Any ideas what I am doing to have an empty self.movieQuotes?
Make sure that the memory management semantic of the property in which you're storing the array is strong, retain, or copy. Note that if you use copy semantics, you'll need to implement the setter method yourself if the property type is NSMutableArray. (It's not clear from your example whether you really expect or need the array to be mutable though.)
Of course you should also double-check to make sure that you have a well-formed plist file named quotes.plist that has an array as its root element, and that it's been properly added to your project's Copy Files build phase.
Edit
Finally, make sure that your viewDidLoad method is actually being called by adding a breakpoint or an NSLog statement.
Related
Just have confusion to improve the code quality in project. Is it right way to assign NSArray object to another NSArray object. But i haven't alloc the one NSArray object.
Is it right below code? Is there any issue for memory management issue?
#import "ViewController.h"
#interface ViewController ()
#property (nonatomic, weak) NSArray *array1;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
NSArray *array2 = [[NSArray alloc] initWithObjects:#"A",#"B",#"C", nil];
NSLog(#"array2 count : %lu",(unsigned long)array2.count);
self.array1 = array2;
NSLog(#"array1 count : %lu",(unsigned long)self.array1.count);
// Do any additional setup after loading the view, typically from a nib.
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
Output :-
2016-04-06 22:22:56.057 PhotoViewer[969:56203] array2 count : 3
2016-04-06 22:22:56.057 PhotoViewer[969:56203] array1 count : 3
My question, Is it required alloc for array1 object?
Ex : self.array1 = [[NSArray alloc] init];
self.array1 = array2;
Your question addresses a core topic which is vital to understand when programming in any language - the difference between value and reference types and semantics.
In summary: for an assignment between two variables of value type the actual bits used to represented the value are copied and after the assignment there is no connection between the two variables - altering one does not effect the other. As the bits are stored directly in the variable there is no need to allocate seperate storage for them. In Objective-C types such as double are value types.
For reference types the bits used to represent the value are stored in storage distinct from the variable and the address of this distinct storage is what is stored in the variable. Assignment copies this address so after assignment the two variables refer to the same value. For mutable values altering the value via one variable effects what the other variable sees as well. Storage for reference types need to be managed separately from the variables, with new storage being allocated, and value bits being copied into it, when a new distinct value is required. Each distinct value is often called an object. For example, in Objective-C NSString is an immutable reference type, while NSMutableArray is a mutable one. In either case new distinct objects are created with alloc/init, new etc.. Simple assignment causes sharing.
In your example the assignment is sharing an immutable array, there is no need to alloc a copy.
There is much written on this, here are some slides from a university which explain it all quite clearly.
HTH
No, it's not needed. array1 is merely a pointer to the memory already pointed to by array2, you don't need to allocate any memory for array1 as the two pointers point to the same memory.
I am doing a tuturial on Lynda.com for objective-c, and ran accross this example code. This is a part of the ViewController.m file. The idea behind the exercise was to create a picker object with custom elements in it.
The following code works just fine and gives me a picker with "happy" and "sad" as the options:
#implementation ViewController
-(NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView{
return 1;
}
-(NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component{
return [[self moods] count];
}
-(NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component{
return self.moods[row];
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.moods = #[#"happy",#"sad"];
}
However, I prefer square brackets to dot syntax and, as you can see I experimented in a few different places with it. Thereturn [[self moods] count was written as return [self.moods count] in the tutorial, but I wanted to use square brackets to verify that it still worked and I understood what was going on, so I changed it and it worked just fine. HOWEVER, I have been trying to do the same thing with the self.moods = #[#"happy",#"sad"]; because I don't like how it looks. I tried:
[[self moods] initWithObjects: #"happy",#"sad", nil];
But I just got a blank picker and a warning "expression result unused". I tried putting _moods = before that expression, and still got a blank picker. What is wrong here?
The reason that [[self moods] initWithObjects: #"happy",#"sad", nil]; is not doing what you expect is due to a misunderstanding in what is happening with regards to dot syntax and how it relates to message sending using square brackets.
Dot syntax is the "syntactic sugar" and recommended way of accessing properties of classes, such as the mood property from your question. Dot syntax is simply a shorthand for accessors (setters / getters) in Objective-C. A quick example might help clear this up.
When dot syntax finds itself on the right hand side of an assignment operator OR as the receiver of a message, the getter method is invoked.
// These two are equivalent
NSArray *allMoods = self.moods
NSArray *allMoods = [self moods]
// These two are equivalent
NSUInteger count = [self.moods count];
NSUInteger count = [[self moods] count];
When dot syntax finds itself on the left hand side of an assignment operator, the setter method is invoked.
// These two are equivalent
self.moods = #[#"happy", #"sad"];
[self setMoods:#[#"happy", #"sad"];
Using dot syntax is not only a nice shorthand, it makes your intentions clearer and newcomers to your code immediately aware that moods is a property of your class.
Also, the reason that [[self moods] initWithObjects: #"happy",#"sad", nil]; is not valid is because -initWithObjects: is an initializer of NSArray that should be called immediately following +alloc. In the piece of code above, [self moods] is returning an NSArray that already exists or lazily instantiating one. For completeness, -initWithObjects should be used as follows:
NSArray *myArray = [[NSArray alloc] initWithObjects:#"happy", #"sad", nil];
I assume you declared #property (strong, nonatomic) NSArray *moods; in the interface since self.moods works.
Setter and getter methods setMoods and getMoods are created automatically.
Here's how the dot syntax boils down to
// These are equivalent
self.moods = #[#"happy",#"sad"];
[self setMoods:#[#"happy",#"sad"]]; // Literal declaration
[self setMoods:[[NSArray alloc] initWithObjects:#"happy",#"sad",nil]]; // Full declaration
This works because you were using the "literal" way of declaring an NSArray* which includes both "allocation" and "initialization".
- (instancetype)initWithObjects: is an instance method which should be called on an instance variable already allocated with alloc. You tried to initialize a variable which has never been allocated in memory.
An slightly cleaner alternative would be:
[self setMoods:[NSArray arrayWithObjects:#"happy",#"sad",nil]];
arrayWithObjects: include both allocation and initialization.
the [self moods] way of referencing it can only be used on the right hand side of an expression, it's calling the getter for the property. self.moods = ... is actually syntactic sugar for [self setMoods:...]
so try [self setMoods:#[#"happy",#"sad"]]
You'll want to read up on the #property declaration and how it "synthesizes" getter and setter methods. What you want to do is "set" the moods property:
[self setMoods: #[#"happy",#"sad"]];
I can't figure out why I keep getting this error. Whenever I press the button in my app the whole thing crashes. Here is the code:
#import "additionViewController.h"
#interface additionViewController (){
}
#end
#implementation additionViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (IBAction)getValue: (id)sender {
self.numberOne = (NSNumber *)self.firstNum.text;
self.numberTwo = (NSNumber *)self.secondNum.text;
self.fnumberOne = [self.numberOne floatValue];
self.fnumberTwo = [self.numberTwo floatValue];
self.finalValue = [NSNumber numberWithFloat:(self.fnumberOne + self.fnumberTwo)];
self.sum.text = (NSString *)self.finalValue;
}
#end
This is the first iOS app I'm trying to make by myself. It's pretty simple. Not going on the App Store or anything.
Assuming firstNum and secondNum are UITextFields - the text properties of those objects will be NSStrings. You can't convert them to numbers by doing a cast to NSNumber. This will just change the type of the pointer but do nothing to actually convert the representation of the data from text to a number.
The simplest solution is:
self.fnumberOne = self.firstNum.floatValue;
self.fnumberTwo = self.secondNum.floatValue;
The floatValue properties will actually convert the text to a float. Then perhaps you don't even need the self.numberOne and self.numberTwo properties? If you still do, you can do this (and similarly with self.finalNumber if you need that too):
self.fnumberOne = self.firstNum.floatValue;
self.fnumberTwo = self.secondNum.floatValue;
self.numberOne = #(self.fnumberOne);
self.numberTwo = #(self.fnumberTwo);
The #() syntax is a convenient way to wrap a primitive in its associated object type, in this case a NSNumber.
Also, you'll have a simlar problem with self.sum.text = (NSString *)self.finalValue - you can't use a C casting operator, you need to actually convert the value back to a string. Simple solution is:
self.sum.text = [NSString stringWithFormat:#"%f", self.fnumberOne + self.fnumberTwo];
There are more advanced ways to convert to/from numeric values using NSNumberFormatter, etc., if you need more robust support. There's plenty of documentation on that.
Go into the project navigator (left window) and go into the breakpoints tab (looks like a carrot) then add an exception breakpoint. This breakpoint should show you where the crash occured. I suspect the problem is trying to cast an NSString to an NSNumber. Use intValue, floatValue, or doubleValue instead.
you need to deallocate the memory you used for these variables
release variableName;
for example, Im not sure what type numberOne, and numberTwo are
There's a little bit uncommon situation in my app, that is,
I have to reload some retain properties everytime when the view is going to appear,
the code looks like this:
// .h
#property (nonatomic, retain) NSArray *myData;
// .m
#synthesize myData;
- (void)viewWillAppear:(BOOL)animated {
... // get FetchRequest and so on
self.myData = [self.context executeFetchRequest:request error:&error]; // Line 1
[super viewWillAppear:animated];
}
- (void)viewDidUnload {
self.myData = nil;
[super viewDidUnload];
}
- (void)dealloc {
[myData release]; // Line 2
[super dealloc];
}
there are several points:
1st. as you see, the property "myData" is retain, so I think every I set some object for it, it would automatically retain that object?
2nd. I have to reload "myData" everytime the view will appear, just like the code of Line 1 above.
3rd. Since it is a retain property, I have to release it myself correctly.
Now, question is, do I correctly managed the memory without any leaking of "myData" using the codes above?
If the view would appear many times before it is dealloc, (like push in a further view in a UINavigationController and pop out for several times),
then myData would retain some object more than once, but I only release it in the dealloc for 1 once in Line 2, so is that ok?
But if I add this method the to viewController,which I think is more safe for avoiding memory leaks:
- (void)viewWillDisappear:(BOOL)animated {
self.myData = nil;
[myData release];
[super viewWillDisappear:animated];
}
- (void)dealloc {
// [myData release]; // don't release it here.
[super dealloc];
}
my app would crash after one or two times I push in and pop out the view,
So which one is really wrong?
Thanks a lot!
You are not only releasing it in Line 2, it will be also released in Line 1 when replaced as well as in viewDidUnload, so your code on top is just fine. The key is that
self.myData = anything;
is expanded to
[self->myData release];
self->myData = [anything retain];
so by assigning anything (including nil) you are already calling release implicitly. You could in fact replace Line 2 with self.myData = nil; to have never to call release since you don't have any explicit retain.
.h
#property (nonatomic, retain) NSArray *myData;
.m
#synthesize myData;
By including these lines in your code a setter and getter is created for your property myData. The setter generated at run time for objects looks something like this,
- (void)setMyData: (id)newValue
{
if (myData != newValue)
{
[myData release];
myData = newValue;
[myData retain];
}
}
The total effect is that whenever you access the property by appending self in front you are actually calling the setters and getters. So the following two lines are the exact same.
self.myData = nil;
[self setMyData:nil];
So your original code was already correct.
i'm a little bit confused with memory management in view controllers.
Lets say i have header file like this:
#interface MyController : UIViewController {
NSMutableArray *data;
}
#property (nonatomic, retain) NSMutableArray *data;
#end
and .m file looks like that:
#implementation MyController
#synthesize data;
- (void)dealloc
{
[self.data release];
[super dealloc];
}
- (void)viewDidLoad
{
[super viewDidLoad];
if (self.data == nil)
self.data = [[NSMutableArray alloc] init];
}
- (void)viewDidUnload
{
[super viewDidUnload];
[self.data release];
self.data = nil;
}
Is that ok from the correct memory management point of view? Will that work after dealloc via Memory Warning? How You do that in your apps?
Thanks for your answers ;)
While the alloc-retain calls balance out in viewDidLoad and viewDidUnload and should prove no problem memory-wise, it would be cleaner to take ownership only once and relinquishing it once rather than twice.
- (void)viewDidLoad
{
[super viewDidLoad];
if (self.data == nil)
self.data = [NSMutableArray array];
}
and
- (void)viewDidUnload
{
[super viewDidUnload];
self.data = nil;
}
You are not guaranteed that viewDidUnload will ever get called. Unlike init/dealloc, which get called in pairs, viewDidUnload is undeterministically called. viewDidUnload is only called if there is a low memory situation and your view is not the active view.
Depending on how your model is created and the implications of it remaining in memory, it may make more sense for you not to get rid of it. An example of this may be that recreating that data may involve an expensive web service call. It therefore would be a bad user experience to have to wait for that data to get recreated. If it must absolutely go, a better strategy may be to cache the data to disk so that you can easily reconstruct it.
viewDidUnload should only contain cleaning up your IBOutlets and flushing easily recreatable data.
These lines from -viewDidUnload both release data:
[self.data release];
self.data = nil;
Since you're using the property setter in the second line, and data is a retained property, the setter will release data. This is an over-release, and it'll cause a crash either right away or later, depending on whether other objects also retain that object. To fix, simply delete the first line and rely on the setter to do the right thing.
The -dealloc method, on the other hand, shouldn't use the setter as it does now. You should change:
[self.data release];
to:
[data release];
data = nil; // this line isn't strictly necessary, but often considered good form
The reasoning here is that it's conceivable that this class could be subclassed, and someone might override the property setter in such a way that it has some side effects that could cause problems when the object is being deallocated. You should access the ivar directly -- notice that I left off the "self." so that we're dealing with the ivar and not the property accessor. (-init and -dealloc are the only places where you have to worry about that; use the property accessors everywhere else.)