I've a theoretical doubt about two type of declaration of a mutable object in iOS (and MacOSX I think) with ARC.
What's the difference between a declaration of an NSMutableArray in the Class Extension, like the code below:
#interface MyViewController ()
#property (copy) NSMutableArray* myMutableArray;
#end
//Class implementation
#implementation MyViewController
...
- (void)viewDidLoad
{
_myMutableArray = [#[] mutableCopy];
}
and a declaration of the same array in this way
#interface MyViewController ()
#property (nonatomic, strong) NSMutableArray* myMutableArray;
#end
//Class implementation
#implementation MyViewController
...
- (void)viewDidLoad
{
_myMutableArray = [#[] mutableCopy];
}
Which one is better? I've seen both versions around and apparently both work fine. However I'd like to know which one is the best option.
I know that the "copy" keyword is to use copy for classes that are part of a class cluster that have mutable/immutable pairs. So in this case, it appear to be the right choice. But the use of the "copy" keyword and the "mutableCopy" property (like the first example) seems a duplicate to me. Am I wrong?
Thanks!
The strong property is the one to use. Since its a mutable object (and is declared as such) then you wouldn't want a copy making, since then things like [self.myArray addObject:object] wouldn't work. You'd use copy properties for immutable objects that may have mutable versions passed in (so an NSString would often be a copy property).
The way the arrays are assigned (making a mutable copy of an empty array made using objective-c literals) is pretty clumsy and would be better written as self.myMutableArray = [NSMutableArray array];
Also, don't access the instance variable directly, use the property accessor.
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 defined variable in GroupView.h
#interface GroupView()
{
NSMutableArray *chatrooms;
}
#end
#implementation GroupView
Now i want to pass this variable in segue
#interface FriendsViewController ()
#end
#implementation FriendsViewController
else if ([segue.identifier isEqualToString:#"showGroupView"]) {
GroupView *groupView = (GroupView *)segue.destinationViewController;
groupView.chatrooms = [NSMutableArray arrayWithArray:chatrooms];
}
i know that chatrooms has to be property in header file to code this way but it is not
So is there any way to use this variable in segue.
Thanks for help.
chatrooms defined as an ivar like you have done is accessed using -> notation:
groupView->chatrooms = [NSMutableArray arrayWithArray:chatrooms]
This is generally discouraged, though. You should use a property instead:
#interface GroupView
#property (strong) NSMutableArray *chatrooms;
#end
Incidentally, if you're using an NSMutableArray, that indicates that you want to modify the element list of the array directly and not just replace the array wholesale. If you only ever want to replace the array with a whole new array every time, I suggest using NSArray instead.
Another point to make here is that you're attempting to cast the object held at segue.destinationViewController as a GroupView. You have either named a UIViewController subclass in a very misleading way, or you are not accessing the GroupView as a correct member of the UIViewController that is returned to you.
Normally, if you are not building the SDK or something. You don't really have a better reason not to expose it in the header file. However, you can expose the property in the extension and declare a private property in the host class(It's really not able to pass if you just declare a local variable). For example, You have a extension called GroupView+Helper. So, you can pass it into the property exposed in the extension. And then internally translate into the GroupView.
In GroupView.m:
#interface GroupView
#property (strong, nonatomic) NSMutableArray *chatrooms;
#end
In GroupView+Helper.h
#property (strong, nonatomic) NSMutableArray *internalChatrooms;
Also, you need to import the GroupView+Helper in the GroupView.
It will make your chatrooms private and internalChatrooms protected.
In the stanford course Paul Hegarty prefers to use lazy instantiation. For instance he makes a private declaration of
#property (strong, nonatomic) (NSArray *)cards
and then he uses the getter to perform an initialization
- (NSArray *) cards
{
if(!_cards) _cards = [[NSArray alloc]init]
return _cards;
}
I'm cool with that. What I don't get though is that at another time Paul declares a public suit for a playingCard being:
#property (strong, nonatomic) NSString *suit;
but in the implementation he doesn't perform this lazy instantiation. So I don't understand where the alloc init of the suit string happens? (suit being a pointer to an NSString - object which ought to get a place in the heap)
#import "PlayingCard.h"
#implementation PlayingCard
#synthesize suit = _suit;
- (void)setSuit:(NSString *)suit
{
if ([#[#"♣︎", #"♠︎", #"♥︎", #"♦︎"]containsObject: suit]) {
_suit = suit;
}
}
- (NSString *)suit
{
return _suit? _suit: #"?";
}
#end
The property is public, so he assumes that it will be set somewhere. When you set this property you can alloc, init and then set it to Playing card instance, for example:
PlayingCard *playingCard = [PlayingCard new];
[playingCard setSuit:#"spade"];
Lazy initialisation is used if property is private (so you can not initialise it outside of the class), but you don't want to initialise it in init method of the class.
When you ask for the cards instance there is no additional information required (i.e. there are no necessary parameters). You just instantiate a PlayingCard and return it.
A suit, on the other hand, could be one of four options, so somebody needs to set that somewhere. Note that this issue is really independent of lazy initialization. It has more to do with the fact that suit expects to be initialized with a user-parameterized value.
Lazy initialization is a way to say, "don't bother creating an instance of this object until I ask for it." But in the case of suit, you don't want to create the string until the user supplies it.
Lazy instantiation is not a panacea but can improve the object instantiation and app responsiveness if you don't spend cycles instantiating ivar objects all at once before you need them. This effect is nothing on a small simple class, but if you have a large set or array of objects, setting them all up completely at once will slow down things at one point.
To be fair the same hit could come later.
This question already has answers here:
Declaration/definition of variables locations in ObjectiveC?
(4 answers)
Closed 9 years ago.
What is the proper way to work with instance variables (declared on interface), their #property and #synthesize, when working in ARC project? What I now do is following:
SomeClass.h:
#interface SomeClass : NSObject {
NSString *someString;
}
#property(nonatomic, copy) NSString* someString;
and SomeClass.m:
#implementation SomeClass
#synthesize someString;
- (void)someMethod {
self.someString = #"Foobar";
}
The thing is that there are other approaches that works, like using just the #property:
SomeClass.h:
#interface SomeClass : NSObject
#property(nonatomic, copy) NSString* someString;
Accessing the someString without self:
SomeClass.m:
#implementation SomeClass
#synthesize someString;
- (void)someMethod {
someString = #"Foobar";
}
etc. I'm new to Objective-c, I'm used to Java. What is the proper way to work with attributes then? I understand that special cases will have special behavior, but what is the best approach in general? (by general I mean I want to access the variable from the class itself and from "outside" and I want ARC to still work correctly, eg. I don't have to worry about memory leaks)
For simple properties, you don't need the instance variable declaration or the #synthesize. The clang compiler will generate both for you by default. So you could write this in the header:
#interface SomeClass : NSObject
#property (nonatomic, copy) NSString *someString;
#end
And the implementation:
#implementation SomeClass
- (void)someMethod {
self.someString = #"Foobar";
}
#end
Avoid direct instance variable access unless you are in the -init method or overriding the setter. Everywhere else you should use the dot syntax (self.someString). If you do need access to the instance variable, the default synthesize will create an underscore-prefixed ivar, e.g. _someString.
Note that for classes with mutable versions like NSString/NSMutableString and NSArray/NSMutableArray the standard practice is to use a copy property. If you use strong on a string or array, the caller might pass in a mutable version and then mutate it from under you, causing hard-to-find bugs.
Check out this SO post for information about ARC.
(Edited) The "strong" attribute tells ARC to keep an object around until the object with the property is deallocated. You do need the "copy" attribute because an NSString property could have been passed in as an NSMutableString. The "copy" guarantees that the original object will be kept around. Again, I apologize for the incorrect/misleading information I originally had here.
The reason you can access the instance variable someString as well as the property self.someString is that the #synthesize someString line creates an instance variable for the property and creates methods for getting and setting the value of it. However, it is recommended that you use the property instead of directly using the instance variable because by using the instance variable, you cannot let the parent object know that you've changed one of its properties.
In my iOS project i am creating an NSArray that can contains integer values. There are several functions in this class that do their task on that NSArray. So i thought to create NSArray as private attribute of that class. In viewDidLoad i am allocating memory to this array i.e
myArray = [[NSArray alloc] init];
myArray will be used in several method of this class. When i analyze my code it shows memory leak as i am allocating myArray and not releasing it.
If i write [myArray release] in dealloc or viewDidUnload warning still there. If i release myArray in last method of class that is using this array, xCode wont allow me to do.
Here is How i am declaring it in my class
.h file
#interface FightVC : UIViewController
{
NSArray *myArray;
}
I want to know what is possible solution of this. other then using autorelease . I don't want to make it public so i am not writing this array as property.
Thanks in advance
Using a private #property as mentioned in the other answer is probably the nicest and cleanest way to do this. Raw instance variables aren’t a very modern way of doing things. However, if you are going down that road, you can implement a getter for your ivar in which you release the old ivar and retain the new one:
- (void)setMyArray:(NSArray *)array {
[myArray release];
myArray = [array retain];
}
That’s the typical pattern anyway (which is what having an #property does for you automatically).
After that, you can create the array, use the setter, and then release the object:
NSArray *newArray = [[NSArray alloc] init];
[self setMyArray:newArray];
[newArray release];
That should keep the analyzer from squawking at you. A few things stick out to me though:
1) [[NSArray alloc] init] isn’t likely to do what you want it to do. It’s going to create an empty, immutable array. You probably either want an NSMutableArray, or you want to instantiate it with objects already in it using a different initializer.
2) NSArrays aren’t really suited for holding integers themselves, they hold objects. You can either use an NSPointerArray or you can put the integers into NSNumbers and put them into an NSArray.
To make a property private, you have to create an anonymous category.
In your .m file:
#interface myClass ()
#property (nonatomic, retain) NSArray *myPrivateArray;
#end
#implementation myClass
// Class code here
#end
To release your array, simply set the property to nil.
self.myPrivateArray = nil;