I'd like to store an array of weak references in Swift. The array itself should not be a weak reference – its elements should be. I think Cocoa NSPointerArray
offers a non-typesafe version of this.
Swift – How to declare an array of weak references in Swift
automatic-ref-countingnspointerarrayswiftweak-references
Related Solutions
It is possible to disable ARC for individual files by adding the -fno-objc-arc
compiler flag for those files.
You add compiler flags in Targets -> Build Phases -> Compile Sources. You have to double click on the right column of the row under Compiler Flags. You can also add it to multiple files by holding the cmd button to select the files and then pressing enter to bring up the flag edit box. (Note that editing multiple files will overwrite any flags that it may already have.)
I created a sample project that has an example: https://github.com/jaminguy/NoArc
See this answer for more info: Disable Automatic Reference Counting for Some Files
Zeroing weak references require OS X 10.7 or iOS 5.
You can only define weak variables in code, ivars or blocks. AFAIK there is no way to dynamically (at runtime) to create a weak variable because ARC takes effect during compile time. When you run the code it already has the retains and releases added for you.
Having said that you can probably abuse blocks to achieve an effect like this.
Have a block that simply returns the reference.
__weak id weakref = strongref;
[weakrefArray addObject:[^{ return weakref; } copy]];
Note that you need to copy the block to get it copied to the heap.
Now you can walk the array anytime you like, dealloc'ed objects in blocks will return nil. You can then remove those.
You cannot have code automatically be executed when the weak ref is zeroed. If this is what you want then you can make use of the function of associated objects. Those get deallocated at the same time as the object they are associated to. So you could have your own sentry tag which informs the weak collection about the objects demise.
You would have one associated object to watch for the dealloc (if the association is the only reference) and the associated object would have a pointer to the collection watching. Then in the sentry dealloc you call the weak collection to inform it that the watched object has gone.
Here's my writeup on associated objects: http://www.cocoanetics.com/2012/06/associated-objects/
Here's my implementation:
---- DTWeakCollection.h
@interface DTWeakCollection : NSObject
- (void)checkInObject:(id)object;
- (NSSet *)allObjects;
@end
---- DTWeakCollection.m
#import "DTWeakCollection.h"
#import "DTWeakCollectionSentry.h"
#import <objc/runtime.h>
static char DTWeakCollectionSentryKey;
@implementation DTWeakCollection
{
NSMutableSet *_entries;
}
- (id)init
{
self = [super init];
if (self)
{
_entries = [NSMutableSet set];
}
return self;
}
- (void)checkInObject:(id)object
{
NSUInteger hash = (NSUInteger)object;
// make weak reference
NSNumber *value = [NSNumber numberWithUnsignedInteger:hash];
[_entries addObject:value];
// make sentry
DTWeakCollectionSentry *sentry = [[DTWeakCollectionSentry alloc] initWithWeakCollection:self forObjectWithHash:hash];
objc_setAssociatedObject(object, &DTWeakCollectionSentryKey, sentry, OBJC_ASSOCIATION_RETAIN);
}
- (void)checkOutObjectWithHash:(NSUInteger)hash
{
NSNumber *value = [NSNumber numberWithUnsignedInteger:hash];
[_entries removeObject:value];
}
- (NSSet *)allObjects
{
NSMutableSet *tmpSet = [NSMutableSet set];
for (NSNumber *oneHash in _entries)
{
// hash is actually a pointer to the object
id object = (__bridge id)(void *)[oneHash unsignedIntegerValue];
[tmpSet addObject:object];
}
return [tmpSet copy];
}
@end
---- DTWeakCollectionSentry.h
#import <Foundation/Foundation.h>
@class DTWeakCollection;
@interface DTWeakCollectionSentry : NSObject
- (id)initWithWeakCollection:(DTWeakCollection *)weakCollection forObjectWithHash:(NSUInteger)hash;
@end
--- DTWeakCollectionSentry.m
#import "DTWeakCollectionSentry.h"
#import "DTWeakCollection.h"
@interface DTWeakCollection (private)
- (void)checkOutObjectWithHash:(NSUInteger)hash;
@end
@implementation DTWeakCollectionSentry
{
__weak DTWeakCollection *_weakCollection;
NSUInteger _hash;
}
- (id)initWithWeakCollection:(DTWeakCollection *)weakCollection forObjectWithHash:(NSUInteger)hash
{
self = [super init];
if (self)
{
_weakCollection = weakCollection;
_hash = hash;
}
return self;
}
- (void)dealloc
{
[_weakCollection checkOutObjectWithHash:_hash];
}
@end
This would be used like this:
NSString *string = @"bla";
@autoreleasepool {
_weakCollection = [[DTWeakCollection alloc] init];
[_weakCollection checkInObject:string];
__object = [NSNumber numberWithInteger:1123333];
[_weakCollection checkInObject:__object];
}
if you output allObjects inside the autorelease pool block then you have two objects in there. Outside you only have the string.
I found that in the dealloc of the entry the object reference is already nil, so you cannot use __weak. Instead I am using the memory address of the object as hash. While these are still in _entries you can treat them as actual object and the allObjects returns an autoreleased array of strong references.
Note: this is not thread safe. Do deal with dealloc's on non-main queues/threads you would need to be careful to synchronize accessing and mutating the internal _entries set.
Note 2: This currently only works with objects checking into a single weak collection since a second check in would overwrite the associated sentry. If you needed this with multiple weak collections then the sentry instead should have an array of those collections.
Note 3: I changed the sentry's reference to the collection to weak as well to avoid a retain cycle.
Note 4: Here are a typedef and helper functions which handle the block syntax for you:
typedef id (^WeakReference)(void);
WeakReference MakeWeakReference (id object) {
__weak id weakref = object;
return [^{ return weakref; } copy];
}
id WeakReferenceNonretainedObjectValue (WeakReference ref) {
if (ref == nil)
return nil;
else
return ref ();
}
Related Topic
- Swift – How to call Objective-C code from Swift
- Swift – #pragma mark in Swift
- Swift – Using a dispatch_once singleton model in Swift
- Swift – How to make a weak protocol reference in ‘pure’ Swift (without @objc)
- Swift Beta performance: sorting arrays
- Swift – How to check if an element is in an array
- IOS app with framework crashed on device, dyld: Library not loaded, Xcode 6 Beta
- PHP – Get a single result using thesqli
Best Answer
Create a generic wrapper as:
Add instances of this class to your array.
When defining
Weak
you can use eitherstruct
orclass
.Also, to help with reaping array contents, you could do something along the lines of:
The use of
AnyObject
above should be replaced withT
- but I don't think the current Swift language allows an extension defined as such.