Weak object in an NSDictionary? - ios

I would like to store a zeroing weak reference to an object in a NSDictionary. This is for a reference to a parent NSDictionary, so I can crawl back up a large structure without searching.
I can not use __weak here; even if my local reference is weak, the NSDictionary will store a strong reference to the object that was weakly referenced. And, of course, NSDictionary can't have nil objects.
I'm on iOS, not Mac, so NSHashTable isn't available. And I only want one object to be weak; the rest should still be strong.
(I'm going to post my answer, so I have something to mark as accepted if there's no better answer. But I'm hoping someone has a better answer.)

In iOS 6+ you can use NSMapTable and choose if you want objects and/or keys to be weak or strong.

I've settled on defining a "container" class with a single member, like this:
#interface Parent : NSObject
#property (nonatomic, weak) id parent;
#end
#implementation Parent
#synthesize parent = _parent;
- (id)initWithParent: (id)parent;
{
if (( self = [super init] )) {
_parent = parent;
}
return self;
}
#end
And using it:
id parentRef = [[Parent alloc] initWithParent: parent];
[newElement setObject: parentRef forKey: ParentKey];
I think this will work for me, but it seems crazy that there's no better way built in to Foundation.

There is a built-in way; just use:
[array addObject:[NSValue valueWithNonretainedObject:object]];
Then to access the object later, use:
id object = [[array objectAtIndex:i] nonretainedObjectValue];
If the value has been released since you added it then "object" will be an invalid pointer under iOS 4.x, and (I'm assuming) will be nil under 5.x with ARC enabled.
EDIT: my assumption was wrong - it's not weak under ARC (see discussion below).

Related

Why are class objects the property attribute of retain and not copy? [duplicate]

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.

why isn't lazy instantiation used in every getter

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.

Apple code example, why are they accessing ivars directly here?

Looking at the Apple example application for MultipeerGroupChat (specifically MainViewController.m):
https://developer.apple.com/library/ios/samplecode/MultipeerGroupChat/Listings/AdhocGroupChat_MainViewController_m.html#//apple_ref/doc/uid/DTS40013691-AdhocGroupChat_MainViewController_m-DontLinkElementID_8
The example contains the below code assigning properties:
#property (retain, nonatomic) NSMutableArray *transcripts;
#property (retain, nonatomic) NSMutableDictionary *imageNameIndex;
and then initializing them in viewDidLoad:
- (void)viewDidLoad
{
[super viewDidLoad];
// Init transcripts array to use as table view data source
_transcripts = [NSMutableArray new];
_imageNameIndex = [NSMutableDictionary new];
---SNIP---
Is there a reason they are assigning directly to the ivars _transcripts and _imageNameIndex? I thought proper convention dictates that you always use the properties to access the variables unless you are in an init method...indeed, a little further in the viewDidLoad method the author does use properties to assign other variables:
self.displayName = [defaults objectForKey:kNSDefaultDisplayName];
self.serviceType = [defaults objectForKey:kNSDefaultServiceType];
I know Apple example code has, in the past, sometimes strayed from good coding practices, so I'm wondering if this is just sloppy coding or if there is a valid reason to access those two ivars directly in a non-initialization method.
There is no definitive way for how properties have to be accessed. Seeing as this is just a demo project, the engineer who wrote this probably knew the scope of the project allowed for some shortcuts.
What's been done here isn't wrong, it just isn't recommended.

iOS "copy" keyword on Mutable objects

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.

where to release NSArray used in different methods of same class

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;

Resources