Does the following code call an accessor "set" function or does it modify the pointer myMember directly?
aClass.h
#interface MyClass : NSObject {
NSArray *myMember;
}
#property (nonatomic, retain) NSArray *myMember;
aClass.c
#implementation GameplayScene
#synthesize myMember;
- (id) init {
if ( (self = [super init]) )
{
myMember = [NSArray array];
}
}
In other words, I would like to know if the method setMyMember is being called, or if the pointer of myMember is being modified directly.
Likewise, is myMember = [NSArray array] identical to self.myMember = [NSArray array]?
Without the self. notation, the instance variable is modified directly. With it, the property setter is called (and since you made it a retain property, the new pointer that it's being set to will be sent a retain message).
See Apple's documentation on declaring and accessing properties.
Related
I do not quite understand the way of declaring instance variable and property. Can someone explain in detail the difference of the two codes below? In the second method, if I use _name for instance variable, is it the same function as the way declaring name in first code? Thanks!
First Code:
// OrderItem.h
#import <Foundation/Foundation.h>
#interface OrderItem : NSObject
{
#public NSString *name;
}
-(id) initWithItemName: (NSString *) itemName;
#end
// OrderItem.m
#import "OrderItem.h"
#implementation OrderItem
-(id) initWithItemName: (NSString *) itemName {
self = [super init];
if (self) {
name = itemName;
NSLog(#"Initializing OrderItem");
}
return self;
}
#end
Second Code:
// OrderItem.h
#import <Foundation/Foundation.h>
#interface OrderItem : NSObject
#property (strong,nonatomic) NSString *name;
-(id) initWithItemName: (NSString *) itemName;
#end
// OrderItem.m
#import "OrderItem.h"
#implementation OrderItem
-(id) initWithItemName: (NSString *) itemName {
self = [super init];
if (self) {
_name = itemName;
NSLog(#"Initializing OrderItem");
}
return self;
}
#end
In the first case you have declared an instance variable (usually called an ivar in Objective-C).
In the second case you have declared a property. A property is a set of two methods, a getter and a setter, usually accessed using dot notation, e.g. self.name. However, an ivar is automatically synthesized for the property with the name _name. That instance variable is what you are accessing in your init.
You can actually change the name of the ivar using #synthesize name = _myName or not have it at all (if you declare the getter and setter manually, no ivar will be synthesized).
Objective-C properties are a rather complicated topic so don't worry if you don't understand it immediately.
Properties are public which means that other classes can read and write them (even classes that aren't subclasses of the class that declares the property). In addition to that, properties provide a getter and a setter method (mutator methods). The getter of a property gets called every time you access the property
NSString *aName = self.name;
Whereas the setter is accessed every time you write or assign to a property.
self.name = #"Some name";
Instance variables (or ivars) are, by default, only visible for the class that declares it and its subclasses (also known as being encapsulated by their class). You can change this default behavior when you add the keyword #public to your ivar declaration though.
I am very much new to objective-c and I'm struggling with this problem for a while! Here is my class prototype:
#interface YoCatchModel : NSObject
/**
Name of the Yo user. Currently this is local
*/
#property (nonatomic, strong) NSString* username;
/**
History of the messages sent with Yo
*/
#property (nonatomic, strong, readonly) NSMutableArray* historyArray;
/*
implement init method
*/
+ (instancetype) initmethod;
I should allocate memory for my history mutable array in this method which is read only.
I want to make another init method that takes a username string parameter. This new initWithUsername method should call init within its definition.
And here is implementation which I am trying to implement an init method using instancetype as the return type. But I am not really sure how to
Allocate memory for the array.
Call another init method for the user name.
#implementation YoCatchModel
+ (instancetype)initmethod {
return [[[self class] alloc] init];
}
I appreciate if anyone can give me some hint how to do this. So far I have read these pages to get to here:
http://www.techotopia.com/index.php/An_Overview_of_Objective-C_Object_Oriented_Programming#Declaring.2C_Initializing_and_Releasing_a_Class_Instance
https://developer.apple.com/library/ios/documentation/cocoa/conceptual/ProgrammingWithObjectiveC/DefiningClasses/DefiningClasses.html#//apple_ref/doc/uid/TP40011210-CH3-SW7
https://developer.apple.com/library/ios/releasenotes/ObjectiveC/ModernizationObjC/AdoptingModernObjective-C/AdoptingModernObjective-C.html#//apple_ref/doc/uid/TP40014150-CH1-SW11
The initWithUsername method becomes the designated initializer of your class and would look something like:
- (instancetype)initWithUsername:(NSString *)username
{
self = [super init];
if (self) {
_username = [username copy];
_historyArray = [NSMutableArray new];
}
return self;
}
You should make the default init method use the designated initializer:
- (instancetype)init
{
return [self initWithUsername:nil];
}
and note that this code works on the property backing instance variables, which start with _, rather than using self. (which won't work with a readonly property anyway), and this is to avoid possible KVO side-effects of the property setter methods.
I want to do something like this:
#property (nonatomic, retain) NSObject *obj1;
#property (nonatomic, retain) NSObject *obj2;
- (id)init {
if ((self = [super init])) {
[SomeClass someFuncWithParam1:*(self.obj1) param2:*(self.obj2)];
}
}
#implementation SomeClass
+ (void)someFuncWithParam1:(NSObject **)param1 param2:(NSObject **)param2 {
//init obj1;
...
//init obj2;
...
}
#end
I haven't found any example how to pass objective-C properties into a function for initialization. I know that it is possible with usual variables but there are no examples about what to do with properties.
You cannot pass an argument by reference in (Objective-)C. What you probably mean is to
pass the address of a variable as an argument to the method, so that the method can
set the value of the variable via the pointer.
However, this does not work with properties.
self.obj1 is just a convenient notation for the method call [self obj1], which returns
the value of the property. And taking the address of a return value is not possible.
What you can do is
Pass the address of the corresponding instance variables:
[SomeClass someFuncWithParam1:&_obj1 param2:&_obj2];
The disadvantage is that the property accessor methods are bypassed. That may or may not be
relevant in your case.
Pass the address of temporary local variables:
NSObject *tmpObj1;
NSObject *tmpObj2;
[SomeClass someFuncWithParam1:&tmpObj1 param2:&tmpObj2];
self.obj1 = tmpObj1;
self.obj2 = tmpObj2;
Both solutions are not very nice. Alternatively, you could pass the entire object (self) to the helper method, which then initializes both properties.
Or you define a custom class with just the properties obj1 and obj2, and make the helper method return an instance of this custom class.
I have an academic question about Class Method exposure. There is something that I obviously don't understand about this and would like some clarification from those in the know.
Background:
I have a simple example of two classes named ViewController and ClassB. Class B contains an array with a method named returnArray. The ViewController accesses the array's data. I have exposed the returnArray method in the ClassB.h file.
Question:
Why is it that I can access the array's data in ViewController without having to define a property? I thought that the property would create a getter to allow access to the array. My example (only exposing the method) allows me to access the data without the creation of the #property.
Class Method:
ClassB.h
#interface ClassB : UIViewController
+(NSArray *) returnArray;
//#property (nonatomic, strong) NSArray *returnArray;
ClassB.m
#implementation ClassB
+(NSArray *) returnArray
{
NSArray *locationArray = #[#"Place1", #"Place2"];
return locationArray;
}
ViewController.m
- (void)viewDidLoad
{
NSArray *location = [ClassB returnArray];
NSLog (#"The count of the location is %d", [location count]);
NSLog (#"The second item in testArray is %#", location[1]);
}
Instance method: After reviewing answers
ClassB.h
*/
{
#private
NSArray *returnArray;
}
- (void)setReturnArray:(NSArray*)returnArray;
-(NSArray *) returnArray;
*/
#property (nonatomic, strong) NSArray *returnArray;
#end
ClassB.m - no change
ViewController.h - no change
ViewController.m
- (void)viewDidLoad
{
[super viewDidLoad];
//Create instance of ClassB
ClassB *classB = [ClassB new];
//Access the instance of returnArray
NSArray *location = [classB returnArray];
NSLog (#"The count of the location is %d", [location count]);
NSLog (#"The second item in testArray is %#", location[1]);
}
#property is a shorthand notation for creating an instance variable and associated accessor methods (with defined access / modification criteria).
What you have is a class method, which internally creates an array and returns it.
That's why you call [ClassB returnArray]; instead of [instanceOfB array];.
These are completely different things. If you wanted to use a property then you would need to create an instance of ClassB and then access the property. This would work, assuming that the array was created when the instance of ClassB was created.
Wain's answer addresses the difference between #property and Class methods, so it's worth a read. My answer assumes you know the difference between class and instance methods, and focuses on the difference between creating a #property versus creating an instance variable with an associate setter and getter.
The reason is because returnArray is a public method that returns an NSArray object on your ClassB.
A #property is merely a convenient way of creating three things at the same time: an instance variable, a setter, and a getter. It has the added bonus of allowing dot-syntax.
But at the end of the day, dot-syntax aside, all you're doing by declaring a #property is equivalently equal to this:
#interface ClassB : NSObject {
#private
NSArray *returnArray;
}
- (void)setReturnArray:(NSArray*)returnArray;
- (NSArray*)returnArray;
This is the same as this:
#property NSArray *returnArray;
Except of course, the dot syntax.
When you do:
NSArray *myArray = classB.returnArray;
You're not actually directly accessing the array you created when you declared the #property.
What you're doing is calling the getter method that was automatically generated when you declared the #property.
I'm making a program where one Class (classA) generates a random number and adds it to a mutable array. A view controller (viewControllerA) calls a method from classA and receives the array of random numbers and stores it in its own array.
I have another class (classB) that needs the same array. After viewcontrollerA is finished doing what it needs to do with the array, it calls the setter method for an array in classB. I call NSLog in the setter and getter methods for the array in classB to check to see if it loads.
-(void)setRandomNumberArray:(NSArray *)randomNumberArray{
_randomNumberArray = randomNumberArray;
NSLog(#"%# setter", _randomNumberArray);
}
-
-(NSArray *)randomNumberArray{
if (!_randomNumberArray) {
_randomNumberArray = [[NSArray alloc] init];
}
NSLog(#"%# getter", _randomNumberArray);
return _randomNumberArray;
}
When I call the setter method in viewControlerA, NSLog returns the value of the array.
When I call the getter method in viewControllerB, NSLog prints nothing from the getter method.
2012-05-29 23:57:43.589 SwipeGame[8603:f803] (
) getter
It's obviously setting the array but not retaining it for when i want to get it. What is going on? I've tried multiple other techniques and it always sets the array but doesn't retain it for when i want to "get" the array.
the property for my array is set to retain btw..
UPDATE:
Yes I am using ARC. my property declaration is:
#property (nonatomic, strong) NSArray *randomNumberArray
SOLVED:
Thanks for all your help! It was a problem with instances.
Your setter method does not mention viewControllerB. You are just setting an internal variable. How is viewControllerB going to know about the array having been set?
The easiest way is to just use #properties and #synthesize:
// in A
viewControllerB.array = _array;
As for the retain question: if you use ARC you should not worry about it.
Do you use ARC?
ARC version bellow:
#interface Foo : NSObject {
NSMutableArray *_randomNumberArray;
}
#property (nonatomic, strong) NSMutableArray *randomNumberArray;
#end
#implementation Foo
#synthesize randomNumberArray = _randomNumberArray;
- (void)setRandomNumberArray:(NSMutableArray *)randomNumberArray {
_randomNumberArray = randomNumberArray;
NSLog(#"%# setter", _randomNumberArray);
}
- (NSMutableArray *) randomNumberArray {
if ( _randomNumberArray == nil )
_randomNumberArray = [[NSMutableArray alloc] init];
NSLog(#"%# getter", _randomNumberArray);
return _randomNumberArray;
}
#end
Not ARC version bellow:
#interface Foo : NSObject {
NSMutableArray *_randomNumberArray;
}
#property (nonatomic, strong) NSMutableArray *randomNumberArray;
#end
#implementation Foo
#synthesize randomNumberArray = _randomNumberArray;
- (void)setRandomNumberArray:(NSMutableArray *)randomNumberArray {
[_randomNumber release];
_randomNumberArray = randomNumberArray;
[_randomNumberArray retain];
NSLog(#"%# setter", _randomNumberArray);
}
- (NSMutableArray *) randomNumberArray {
if ( _randomNumberArray == nil )
_randomNumberArray = [[NSMutableArray alloc] init];
NSLog(#"%# getter", _randomNumberArray);
return _randomNumberArray;
}
- (void)dealloc {
[_randomNumberArray release];
}
#end
If you are creating this random number array using an NSMutableArray, and passing that to the setter, the array could be mutated later by the caller (e.g. all items removed) and the array can change from under your feet.
For types like NSArray and NSString which have mutable subtypes, you should declare your property as copy instead of strong. This will ensure the array passed to you cannot be mutated at a later date by somebody else. Copy performance is not a problem because the regular immutable types handle copies very efficiently.