[ACCEPTED]-NSArray of weak references (__unsafe_unretained) to objects under ARC-automatic-ref-counting
As Jason said, you can't make NSArray
store weak 10 references. The easiest way to implement 9 Emile's suggestion of wrapping an object 8 inside another object that stores a weak 7 reference to it is the following:
NSValue *value = [NSValue valueWithNonretainedObject:myObj];
[array addObject:value];
Another 6 option: a category that makes NSMutableArray
optionally store 5 weak references.
Note that these are "unsafe 4 unretained" references, not self-zeroing 3 weak references. If the array is still around 2 after the objects are deallocated, you'll 1 have a bunch of junk pointers.
The solutions to use a NSValue helper or to create 17 a collection (array, set, dict) object and 16 disable its Retain/Release callbacks are both not 100% failsafe solutions with 15 regard to using ARC.
As various comments 14 to these suggestions point out, such object 13 references will not work like true weak 12 refs:
A "proper" weak property, as 11 supported by ARC, has two behaviors:
- Doesn't hold a strong ref to the target object. That means that if the object has no strong references pointing to it, the object will be deallocated.
- If the ref'd object is deallocated, the weak reference will become nil.
Now, while 10 the above solutions will comply with behavior 9 #1, they do not exhibit #2.
To get behavior 8 #2 as well, you have to declare your own 7 helper class. It has just one weak property 6 for holding your reference. You then add 5 this helper object to the collection.
Oh, and 4 one more thing: iOS6 and OSX 10.8 supposedly 3 offer a better solution:
[NSHashTable weakObjectsHashTable]
[NSPointerArray weakObjectsPointerArray]
[NSPointerArray pointerArrayWithOptions:]
These should give 2 you containers that hold weak references 1 (but note matt's comments below).
I am new to objective-C, after 20 years 13 of writing c++.
In my view, objective-C 12 is excellent at loosely-coupled messaging, but 11 horrible for data management.
Imagine how 10 happy I was to discover that xcode 4.3 supports 9 objective-c++!
So now I rename all my .m 8 files to .mm (compiles as objective-c++) and 7 use c++ standard containers for data management.
Thus 6 the "array of weak pointers" problem becomes 5 a std::vector of __weak object pointers:
#include <vector>
@interface Thing : NSObject
@end
// declare my vector
std::vector<__weak Thing*> myThings;
// store a weak reference in it
Thing* t = [Thing new];
myThings.push_back(t);
// ... some time later ...
for(auto weak : myThings) {
Thing* strong = weak; // safely lock the weak pointer
if (strong) {
// use the locked pointer
}
}
Which 4 is equivalent to the c++ idiom:
std::vector< std::weak_ptr<CppThing> > myCppThings;
std::shared_ptr<CppThing> p = std::make_shared<CppThing>();
myCppThings.push_back(p);
// ... some time later ...
for(auto weak : myCppThings) {
auto strong = weak.lock(); // safety is enforced in c++, you can't dereference a weak_ptr
if (strong) {
// use the locked pointer
}
}
Proof of 3 concept (in the light of Tommy's concerns 2 about vector reallocation):
main.mm:
#include <vector>
#import <Foundation/Foundation.h>
@interface Thing : NSObject
@end
@implementation Thing
@end
extern void foo(Thing*);
int main()
{
// declare my vector
std::vector<__weak Thing*> myThings;
// store a weak reference in it while causing reallocations
Thing* t = [[Thing alloc]init];
for (int i = 0 ; i < 100000 ; ++i) {
myThings.push_back(t);
}
// ... some time later ...
foo(myThings[5000]);
t = nullptr;
foo(myThings[5000]);
}
void foo(Thing*p)
{
NSLog(@"%@", [p className]);
}
example 1 log output:
2016-09-21 18:11:13.150 foo2[42745:5048189] Thing
2016-09-21 18:11:13.152 foo2[42745:5048189] (null)
If you do not require a specific order you 5 could use NSMapTable
with special key/value options
NSPointerFunctionsWeakMemory
Uses 4 weak read and write barriers appropriate 3 for ARC or GC. Using NSPointerFunctionsWeakMemory 2 object references will turn to NULL on last 1 release.
I believe the best solution for this is 3 to use NSHashTable or NSMapTable. the Key 2 or/and the Value can be weak. You can read 1 more about it here: http://nshipster.com/nshashtable-and-nsmaptable/
The simplest solution:
NSMutableArray *array = (__bridge_transfer NSMutableArray *)CFArrayCreateMutable(nil, 0, nil);
NSMutableDictionary *dictionary = (__bridge_transfer NSMutableDictionary *)CFDictionaryCreateMutable(nil, 0, nil, nil);
NSMutableSet *set = (__bridge_transfer NSMutableSet *)CFSetCreateMutable(nil, 0, nil);
Note: And this works on 1 iOS 4.x too.
To add weak self reference to NSMutableArray, create 6 a custom class with a weak property as given 5 below.
NSMutableArray *array = [NSMutableArray new];
Step 1: create a custom class
@interface DelegateRef : NSObject
@property(nonatomic, weak)id delegateWeakReference;
@end
Step 2: create a method to add self as weak reference to NSMutableArray. But here we add the DelegateRef object
-(void)addWeakRef:(id)ref
{
DelegateRef *delRef = [DelegateRef new];
[delRef setDelegateWeakReference:ref]
[array addObject:delRef];
}
Step 3: later on, if the property 4 delegateWeakReference == nil
, the object can be removed from the array 3
The property will be nil, and the references 2 will be deallocated at proper time independent 1 of this array references
No, that's not correct. Those aren't actually 9 weak references. You can't really store 8 weak references in an array right now. You 7 need to have a mutable array and remove 6 the references when you're done with them 5 or remove the whole array when you're done 4 with it, or roll your own data structure 3 that supports it.
Hopefully this is something 2 that they'll address in the near future 1 (a weak version of NSArray
).
I've just faced with same problem and found 15 that my before-ARC solution works after 14 converting with ARC as designed.
// function allocates mutable set which doesn't retain references.
NSMutableSet* AllocNotRetainedMutableSet() {
CFMutableSetRef setRef = NULL;
CFSetCallBacks notRetainedCallbacks = kCFTypeSetCallBacks;
notRetainedCallbacks.retain = NULL;
notRetainedCallbacks.release = NULL;
setRef = CFSetCreateMutable(kCFAllocatorDefault,
0,
¬RetainedCallbacks);
return (__bridge NSMutableSet *)setRef;
}
// test object for debug deallocation
@interface TestObj : NSObject
@end
@implementation TestObj
- (id)init {
self = [super init];
NSLog(@"%@ constructed", self);
return self;
}
- (void)dealloc {
NSLog(@"%@ deallocated", self);
}
@end
@interface MainViewController () {
NSMutableSet *weakedSet;
NSMutableSet *usualSet;
}
@end
@implementation MainViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
weakedSet = AllocNotRetainedMutableSet();
usualSet = [NSMutableSet new];
}
return self;
}
- (IBAction)addObject:(id)sender {
TestObj *obj = [TestObj new];
[weakedSet addObject:obj]; // store unsafe unretained ref
[usualSet addObject:obj]; // store strong ref
NSLog(@"%@ addet to set", obj);
obj = nil;
if ([usualSet count] == 3) {
[usualSet removeAllObjects]; // deallocate all objects and get old fashioned crash, as it was required.
[weakedSet enumerateObjectsUsingBlock:^(TestObj *invalidObj, BOOL *stop) {
NSLog(@"%@ must crash here", invalidObj);
}];
}
}
@end
Output:
2013-06-30 13 00:59:10.266 not_retained_collection_test[28997:907] constructed 12 2013-06-30 00:59:10.267 not_retained_collection_test[28997:907] addet 11 to set 2013-06-30 00:59:10.581 not_retained_collection_test[28997:907] constructed 10 2013-06-30 00:59:10.582 not_retained_collection_test[28997:907] addet 9 to set 2013-06-30 00:59:10.881 not_retained_collection_test[28997:907] constructed 8 2013-06-30 00:59:10.882 not_retained_collection_test[28997:907] addet 7 to set 2013-06-30 00:59:10.883 not_retained_collection_test[28997:907] deallocated 6 2013-06-30 00:59:10.883 not_retained_collection_test[28997:907] deallocated 5 2013-06-30 00:59:10.884 not_retained_collection_test[28997:907] deallocated 4 2013-06-30 00:59:10.885 not_retained_collection_test[28997:907] * -[TestObj respondsToSelector:]: message 3 sent to deallocated instance 0x1f03c8c0
Checked 2 with iOS versions 4.3, 5.1, 6.2. Hope it 1 will be useful to somebody.
If you need zeroing weak references, see this answer for code 4 you can use for a wrapper class.
Other answers 3 to that question suggest a block-based wrapper, and ways 2 to automatically remove zeroed elements 1 from the collection.
If you use a lot this comportment it's indicated 4 to your own NSMutableArray class (subclass 3 of NSMutableArray) which doesn't increase 2 the retain count.
You should have something 1 like this:
-(void)addObject:(NSObject *)object {
[self.collection addObject:[NSValue valueWithNonretainedObject:object]];
}
-(NSObject*) getObject:(NSUInteger)index {
NSValue *value = [self.collection objectAtIndex:index];
if (value.nonretainedObjectValue != nil) {
return value.nonretainedObjectValue;
}
//it's nice to clean the array if the referenced object was deallocated
[self.collection removeObjectAtIndex:index];
return nil;
}
More Related questions
We use cookies to improve the performance of the site. By staying on our site, you agree to the terms of use of cookies.