I'm using a property of NSArray type. Then I'm trying to initialize or setting values for the NSArray. When I use shorthand assignment, I'm getting the output. But when I'm trying with long initialization style, I'm not getting the result. What should be the right way for the latter??
Here is the code snippet:
#property NSArray * moods;
//shorthand assignment
self.moods=#[#"Happy",#"Sad"];
NSLog(#"Hello %#",[self moods]);
This is working. But when I tried:
//long initialization style
[[self moods]initWithObjects:#"Happy",#"Sad", nil];
NSLog(#"Hello %#",[self moods]);
This isn't doing the same way. Suggest me something please.
The second example should be:
self.moods = [[NSArray alloc] initWithObjects:#"Happy",#"Sad", nil];
alloc must always be called before init to actually allocate the memory for the object. [self moods] is going to return nil until you assign something to self.moods.
Edit:
If you really want to avoid the assignment by property dot notation syntax for whatever reason, you can use the setter method instead:
[self setMoods: [[NSArray alloc] initWithObjects:#"Happy",#"Sad", nil]];
The answer above is completely correct. I would love just to add a comment for the sake of completeness but I can't so I'll add an extra answer to give all the options.
You can use the convenience initializers if you always get confused with the order of the alloc and init. Or if you want to have cleaner code.
self.moods = [NSArray arrayWithObjects:#"Happy",#"Sad", nil];
But the answer above it's perfect and I personally prefer the more explicit alloc init pattern.
just some alternative approach without dots... ;)
[self setMoods:#[#"Happy", #"Sad"];
Related
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"]];
Is it safe to have a method edit a parameter and not be inside a category.
For example, just having a method like this in a ViewController subclass:
-(void)editArray:(NSMutableArray*)array
{
[array removeLastObject];
}
And calling [self editArray:(someArray)]; inside the view controller where someArray is a strong atomic property. It seems to work when I test it, I just don't know if this is discouraged. I know I could easily do this in a category, I just want to know if something like this is safe.
This is safe, as long you're not modifying the reference, just the contents of the parameter object. E.g. assigning to array inside the method would not work, you'd need to pass it by reference instead. As regards style, consider how the errors are returned from many Cocoa Touch framework methods:
NSError *error = nil;
[NSFileManager defaultManager] removeItemAtPath:filePath error:&error];
Here, too, the parameter object, in this case &error, is modified inside the method. So I think you're good from the style perspective as well :)
If you change the array while it is being mutated Cocoa will throw an exception *** Collection <__NSArrayM: 0x7f96432004c0> was mutated while being enumerated.
Consider the code:
NSMutableArray* a = [[NSMutableArray alloc] initWithArray:#[#"a", #"b", #"c"]];
for (id x in a) {
[a removeLastObject];
}
In this case it would be unsafe and cause your application to crash. Abstracting the removeLastObject message into another message doesn't really change anything.
It will also not be safe if done on a background thread.
I'm trying the following code to create an instance, assign properties, add to array.
Then, assigning new properties and adding again.
However array will contain 2 identical objects (equal to the second one added). The class Message simply has several (nonatomic, retain) NSStrings/Integer properties.
This probably has something to do with my understanding of pointer, can someone explain?
self.messages=[[NSMutableArray alloc]init];
Message *m=[[Message alloc]init];
m.cb=#"2402";
m.ck=1001;
m.msg=#"as";
[self.messages addObject:m];
m.cb=#"2422";
m.ck=1002;
m.msg=#"aadfsdsdfdssdklsdflkh";
[self.messages addObject:m];
NSLog(#"%#",self.messages);
When you add an object to an array, it does not add a copy of the object to the array, but instead just a reference to it. If you want two different objects, then you need to create two different objects instead of re-using the same one (or, as #Brendon points out, create a copy when you add it to your array).
To fix your example, the most common technique would be to add the following line right before you start modifying the properties for the second object:
m=[[Message alloc]init];
Or, use a second pointer and object instead of reusing m.
EDIT:
To add a copy, change [self.messages addObject:m]; to [self.messages addObject:[m copy]];, assuming that the Message class conforms to the NSCopying protocol.
Yes, after executing the posted code self.messages contains the Message object twice, at indexes 0 and 1. That's not a problem, though. Arrays can contain any object, even themselves.
It seems that you want two distict objects, so you would just create a second Message.
You can either implement the NSCopy protocol — as mentioned by lnafziger — or just create new instances quite easily in a for loop.
«Two or more, use a for»
— Edsger W. Dijkstra
self.messages=[[NSMutableArray alloc] init];
NSArray *dataArray = #[ #{#"cb": #"2402", #"ck": #(1001), #"msg": #"as"},
#{#"cb": #"2422", #"ck": #(1002), #"msg": #"aadfsdsdfdssdklsdflkh"}
];
for(NSDictionary *data in dataArray) {
Message *m=[[Message alloc] init];
m.cb = data[#"cb"];
m.ck = [data[#"ck"] integerValue];
m.msg = data[#"msg"];
[self.messages addObject:m];
}
Could someone share some knowledge on whats best practice / code convention on using #property iVars in init methods or designated initializers?
please see my example:
#interface MyClass ()
#property(nonatomic,strong) nsstring *tempString;
#property(nonatomic,strong) NSMutableArray *arrItems;
#end
#implementation ViewController
- (id)init
{
if (self = [super init]) {
//Is this best practice / correct
_tempString = #"";
_arrItems = [[NSMutableArray alloc] initWithCapacity:0];
...
...
//Or this
self.tempString = #"";
self.arrItems = [[NSMutableArray alloc] initWithCapacity:0];
}
return self;
}
#end
Any advice on why one or the other should be used?
Thanks...
Apple's guidance on this topic is included in the aptly named section Don’t Use Accessor Methods in Initializer Methods and dealloc.
Read this thread: Why shouldn't I use Objective C 2.0 accessors in init/dealloc?
In other words if you are not goiung to use KVO you can use second approach:
//Or this
self.tempString = #"";
self.arrItems = [[NSMutableArray alloc] initWithCapacity:0];
But be care full with alloc-init, don't forget about autorelease.
It's typically better to use property notation when you define it, partly(mostly?) for the reason Jeremy mentioned.
Debugging a particular variable is a whole lot easier when you can set a breakpoint in method setter override and have it apply to ALL code paths that modify the variable.
Another reason is to keep a consistent memory management model, although it is less important since you are using ARC. If you weren't however, and strong was retain, then you would make sure that the object you are setting to the property is autoreleased everywhere you set the property, and not have to deal with releasing the current value if you are directly setting the variable.
Consistency is important for maintenance/readability and debugging, no matter what practices you use.
I prefer the lazy instantiation method for properties.
After you #synthesize you can override your getter to lazily instantiate your property
For Example:
-(NSString *)tempString {
if(!tempString) {
_tempString = #"";
}
return _tempString;
}
and
-(NSMutableArray *)arrItems {
if(!_arrItems) {
_arrItems = [[NSMutableArray alloc] initWithCapacity:0];
}
return _arrItems;
}
If you do want to set your property in the init method, use dot notation self.myProperty so that it uses the defined setter for the property and not the private class method directly.
According to Apple, you should not use accessors in init... or dealloc methods:
You should always access the instance variables directly from within
an initialization method because at the time a property is set, the
rest of the object may not yet be completely initialized. Even if you
don’t provide custom accessor methods or know of any side effects from
within your own class, a future subclass may very well override the
behavior.
Taken from this doc: Encapsulating Data.
I'm trying to switch views in my app using this chunk of code:
self->variable1 = [[NSNumber alloc] initWithInt:0];
self->variable2 = [[NSMutableArray arrayWithCapacity:1];
self->variable3 = [[NSMutableArray arrayWithCapacity:1];
[self presentModalViewController:titleScreen animated:YES];
If I comment out all of the allocated variable lines, the code works fine. If it leave just 1 line in the code crashes with the "EXC_BAD_ACCESS" error. Why is this happening? The variables aren't being used at all, just declared for later use. I'm not getting any compile errors on the lines either. What am I doing wrong?
UPDATE:
Thank you everyone for the help. I change the way I declare my variables to #property/#synth to clean up my code, but it didn't fix the problem. After a long time of fiddling I fixed it. I changed the code from this:
self.variable1 = [[NSNumber alloc] initWithInt:0];
to this:
self.variable1 = [NSNumber alloc];
[self.variable1 initWithInt:0];
and it worked! Can someone explain why this worked and the first line didn't?
Update:
Thank you Peter Hosey for showing me my evil ways. This time I'm pretty sure it's fixed. I was storing my variable Releases in
-(void)release
I didn't realize xCode will release when it needs to. I moved all the variable releases to
-(void)Destroy
so I can release everything on MY command. Now the code works. Thanks again!
I suggest that you declare variable1, variable2, and variable3 as properties, not instance variables. Then, use self.variable1, self.variable2, and self.variable3 to access them.
The dot syntax (self.variable1, etc.) uses the memory management policy you declared on each property; the arrow syntax (self->variable1, etc.) will access the variables directly. The crash is because you created two arrays in away that doesn't leave you owning them, and then did not assign the arrays to a property that would retain them.
You may also want to upgrade your project to use ARC. Then there is no memory-management difference; assigning to the instance variables rather than the properties will not cause the object to be prematurely released, because ARC considers instance variables to be ownerships by default. You may still want to switch to using properties after you switch to ARC, but not to prevent a crash.
In response to your edit:
I change the way I declare my variables to #property/#synth to clean up my code, but it didn't fix the problem.
Then something else was wrong.
You never did say much about the problem itself. You said you got an EXC_BAD_ACCESS, but not what statement triggered the crash or on what grounds you blamed it on the code you showed.
I changed the code from this:
self.variable1 = [[NSNumber alloc] initWithInt:0];
That's the correct code, though. That's what you should be using.
to this:
self.variable1 = [NSNumber alloc];
[self.variable1 initWithInt:0];
Noooo! That code is wrong, wrong, wrong, on multiple levels.
init methods (including initWithWhatever: methods) are not guaranteed to return the same object you sent the message to. NSNumber's initWithInt: very probably doesn't.
That object creates an uninitialized NSNumber object and assigns that to the property. Then it sends initWithInt: to that object, which will return an initialized object, which can be and very probably will be a different object. Now you are holding an uninitialized object (which you will try to use later) and have dropped the initialized object on the floor.
Never, ever, ever send alloc and init(With…) in separate expressions. Always send them in the same expression. No exceptions. Otherwise, you risk holding the uninitialized object rather than the initialized object. In your case (with NSNumbers), that is almost certainly what will happen.
What you should be doing is declaring and synthesizing a strong property that owns the NSNumber object, and creating the NSNumber object in a single statement: either [[NSNumber alloc] initWithInt:] or [NSNumber numberWithInt:]. If you're not using ARC, you'll want the latter, since the property will retain the object. If you are using ARC, they're effectively equivalent.
And if you get a crash with that code, then something else is wrong, so please tell us—either in this question or in a new question—about the crash so we can help you find the true cause of it.
variable2 and variable3 are being autoreleased before you actually access them (presumably) later after presenting the modal view.
At the very least change the lines to:
self->variable2 = [[NSMutableArray arrayWithCapacity:1] retain];
self->variable3 = [[NSMutableArray arrayWithCapacity:1] retain];
or
self->variable2 = [[NSMutableArray alloc] initWithCapacity:1];
self->variable3 = [[NSMutableArray alloc] initWithCapacity:1];
variable1 should be fine.
Best would be to use #property and #synthesize so you can use dot notation:
.h
#interface MyClass : SuperClass
#property (nonatomic,retain) NSMutableArray *variable2;
#property (nonatomic,retain) NSMutableArray *variable3;
#end
.m
#implementation MyClass
#synthesize variable2,varible3;
- (void)foo {
self.variable2 = [NSMutableArray arrayWithCapacity:1];
self.variable3 = [NSMutableArray arrayWithCapacity:1];
}
#end
By default, all instance variables in objective-c have protected scope. So unless you have explicitly declared them public in your interface file as:
#interface MYClass {
#public
NSNumber *variable1;
NSMutableArray *variable2;
NSMutableArray *variable3;
}
//...
#end
then they will not be accessible using the struct dereferencing operator. This is likely the cause of those EXC_BAD_ACCESS errors.