70

I need to store weak references to objects in an NSArray, in order to prevent retain cycles. I'm not sure of the proper syntax to use. Is this the correct way?

Foo* foo1 = [[Foo alloc] init];
Foo* foo2 = [[Foo alloc] init];

__unsafe_unretained Foo* weakFoo1 = foo1;
__unsafe_unretained Foo* weakFoo2 = foo2;

NSArray* someArray = [NSArray arrayWithObjects:weakFoo1, weakFoo2, nil];

Note that I need to support iOS 4.x, thus the __unsafe_unretained instead of __weak.


EDIT (2015-02-18):

For those wanting to use true __weak pointers (not __unsafe_unretained), please check out this question instead: Collections of zeroing weak references under ARC

Community
  • 1
  • 1
Emile Cormier
  • 26,080
  • 13
  • 87
  • 116

12 Answers12

75

As Jason said, you can't make NSArray store weak references. The easiest way to implement Emile's suggestion of wrapping an object inside another object that stores a weak reference to it is the following:

NSValue *value = [NSValue valueWithNonretainedObject:myObj];
[array addObject:value];

Another option: a category that makes NSMutableArray optionally store weak references.

Note that these are "unsafe unretained" references, not self-zeroing weak references. If the array is still around after the objects are deallocated, you'll have a bunch of junk pointers.

Community
  • 1
  • 1
yuji
  • 16,533
  • 4
  • 60
  • 62
  • My pleasure! Also, check out my updated answer for another option. – yuji Feb 17 '12 at 23:02
  • I think a category on `NSMutableArray` is a bad idea, since it's a class cluster and you'll be going down a long rabithole of issues. Far better to create your own `NSObject` subclass that has all the same methods as `NSMutableArray`. – Abhi Beckert Feb 18 '12 at 00:16
  • 1
    Have you looked at the actual solution I linked to? I'm no expert on `NSMutableArray` and class clusters, but it seems to be setting up an additional factory method that use a specific backing store, while not affecting any of the other class methods. – yuji Feb 18 '12 at 00:22
  • 16
    NOTE: these aren't self-zeroing weak references. They just don't get retained. – mxcl Oct 22 '12 at 17:12
  • Yeah, as @mxcl said, these are unsafe unretained, not weak. So it will crash if you will try to unwrap the value after it's deallocated. Use NSMapTable and NSHashTable instead. – Mikhail Larionov Jun 30 '14 at 07:03
  • How to restore myObj from NSValue? @yuji – Beyond Chao Mar 16 '18 at 04:44
  • @BeyondChao use `nonretainedObjectValue` property – Gon Jul 30 '18 at 07:35
59

The solutions to use a NSValue helper or to create a collection (array, set, dict) object and disable its Retain/Release callbacks are both not 100% failsafe solutions with regard to using ARC.

As various comments to these suggestions point out, such object references will not work like true weak refs:

A "proper" weak property, as supported by ARC, has two behaviors:

  1. 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.
  2. If the ref'd object is deallocated, the weak reference will become nil.

Now, while the above solutions will comply with behavior #1, they do not exhibit #2.

To get behavior #2 as well, you have to declare your own helper class. It has just one weak property for holding your reference. You then add this helper object to the collection.

Oh, and one more thing: iOS6 and OSX 10.8 supposedly offer a better solution:

[NSHashTable weakObjectsHashTable]
[NSPointerArray weakObjectsPointerArray]
[NSPointerArray pointerArrayWithOptions:]

These should give you containers that hold weak references (but note matt's comments below).

Community
  • 1
  • 1
Thomas Tempelmann
  • 9,137
  • 6
  • 60
  • 120
  • `[NSPointer weakObjectsPointerArray]` on iOS 6 is still *not* ARC `__weak` references. So this is *not* a better solution: like the solutions you critique, it complies with behavior #1 but not #2. It's elegant and convenient, but it's not ARC `__weak` references. – matt Nov 30 '12 at 05:31
  • @matt How did you find that weakObjectsPointerArray does not return __weak refs? Did you test it in code or do you refer to the "NSPointerArray Class Reference" documentation saying "Important: NSPointerArray does not support weak references under Automatic Reference Counting (ARC)."? I had wondered if that wasn't just a leftover from pre-10.8 times and is a documentation error. – Thomas Tempelmann Nov 30 '12 at 09:22
  • 1
    Well, I never imagined that a huge warning in a box would be "just a leftover". The references are clearly weak, and they are clearly made NULL when the object has been released, but I don't know how to test that they are `__weak` in the full ARC sense. I'll ask. – matt Nov 30 '12 at 15:48
  • 2
    If they become NULL when their object gets dealloc'd, then they fit my behavior #2, and then all is good. Before ARC, a weak ref was just a pointer (type id) that was left dangling once its referenced object got deallocated. ARC's weak ref is smarter in the way that it then becomes NULL, making it safer to use. So, if that's the behavior you see, then the warning box in the docs is indeed a mistake. I had also sent a feedback about this, asking them to double check. Unfortunately, they (Apple) don't give you feedback :( – Thomas Tempelmann Dec 04 '12 at 10:36
  • My experience conflicts – I end up with pointers to deallocated instances in both `[NSPointerArray pointerArrayWithWeakObjects]` and `[NSMapTable mapTableWithStrongToWeakObjects]`. I posted a more specific question here: http://stackoverflow.com/questions/14209070/collections-of-zeroing-weak-references-under-arc – paulmelnikow Jan 08 '13 at 06:06
  • @matt With `[NSPointerArray weakObjectsPointerArray]` you do get an array of true ARC weak references. References do get nullified. I just checked. – Kristian Spangsege May 08 '14 at 13:12
  • @KristianSpangsege Did you check on iOS or OS X? And what version? – Ashley Jun 14 '14 at 12:39
  • Please note the following line in the documentation "In a garbage collected environment, if you specify a zeroing weak memory configuration, if an element is collected it is replaced by a NULL value". Remember that ARC is not garbage collection! – Voxar Sep 22 '14 at 10:29
  • I found that nil'ed objects are removed instantly from the `[NSHashTable weakObjectsHashTable]` in opposite to overwritten objects pointers, when table can still holds the old reference until the next after next table operation like addObject: but which could be the code/environment specific behaviour (I didn't tested this). – Krzysztof Przygoda Mar 23 '15 at 08:03
  • 2
    BTW, If you look into `NSHashTable` header for `+ (NSHashTable *)weakObjectsHashTable;` you'll find a comment: `// entries are not necessarily purged right away when the weak object is reclaimed`. – Krzysztof Przygoda Mar 23 '15 at 08:14
28

I am new to objective-C, after 20 years of writing c++.

In my view, objective-C is excellent at loosely-coupled messaging, but horrible for data management.

Imagine how happy I was to discover that xcode 4.3 supports objective-c++!

So now I rename all my .m files to .mm (compiles as objective-c++) and use c++ standard containers for data management.

Thus the "array of weak pointers" problem becomes 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 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 concept (in the light of Tommy's concerns 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 log output:

2016-09-21 18:11:13.150 foo2[42745:5048189] Thing
2016-09-21 18:11:13.152 foo2[42745:5048189] (null)
Richard Hodges
  • 64,204
  • 6
  • 75
  • 124
  • 14
    Yeah, but this is not a true objective C solution. you just like it cause you're a c++ guy. – Aran Mulholland Jun 25 '14 at 13:10
  • 10
    Aran, I am a guy who seeks to use the best available tools for the job. objective-c does not have good data handling capabilities, being essentially C with some messaging hacks and a massive supporting library. It's an anachronistic and obsolete language (hence apple seeking to replace it with SWIFT). Since we have the opportunity to use the excellent c++ compiler that ships with xcode, why not take it? – Richard Hodges Jun 26 '14 at 10:02
  • 2
    This is not Objective-c as much as Objective-C is not C. It's supported natively by the compiler and has better memory management capabilities – André Fratelli Jul 05 '15 at 02:06
  • Interesting! I fear you will make many Objective-C users very unhappy, though... Come to think of it, a nice wrapper that feels like NSArray might just solve that. – Timo Oct 02 '15 at 07:35
  • 2
    I wrap all objective-c objects in lightweight c++ wrappers. It makes for safe code and completely obviates Apple's latest idiocy - Swift. – Richard Hodges Oct 02 '15 at 07:55
  • Slow as I am: *I strongly believe that this approach is invalid*. Objective-C's `weak` builds a table on the side of the thing being pointed to of all pointers to it. The thing that is deallocated is responsible for nilling weak references to it. Ensuring proper management of that table (e.g. when weak pointers go out of scope or cease to exist) is ARC's job. `std::vector` may reallocate memory, which would change the location of the pointers it contains but it doesn't understand ARC so doesn't update the target table. Therefore it may leave dangling pointers at the target. – Tommy Sep 21 '16 at 15:16
  • @Tommy if it reallocated memory it must move or copy/destroy objects. If these objects are arc-enabled NSValue pointers, they will simply undergo the normal reference counting. – Richard Hodges Sep 21 '16 at 15:18
  • `NSValue` isn't used here (and can't hold weak references in any case). In C++ it would work because the objects themselves would handle the copying. In Objective-C you need the ARC compiler to see the moves and the end-of-lifetimes. I don't think it will spot this. – Tommy Sep 21 '16 at 15:23
  • @Tommy apologies, I of course meant `NSObject`. `std::vector` is (objective)-c++. Not objective-c. I export objective-c interfaces but implement them with objective-c++. All implementation goes in the .mm file. – Richard Hodges Sep 21 '16 at 15:25
  • I'm arguing that I don't think that the ARC compiler is smart enough to spot and account for the effect of `std::vector` resizes. The pointers themselves contain no logic — there's no lock operation because it's a no-op — and `std::vector` is predicated on that assumption. – Tommy Sep 21 '16 at 15:30
  • @Tommy let's have a look at some assembler output and see – Richard Hodges Sep 21 '16 at 15:33
  • @Tommy https://gist.github.com/madmongo1/c18a3a567055481762b123629b0135d0 – Richard Hodges Sep 21 '16 at 15:49
  • I'm not sure how that code is supposed to prompt a vector resize, during which the vector ends up needing to use a new patch of memory rather than merely expanding the old, which is the operation I'm worried about; will try to mess about with it later though. – Tommy Sep 21 '16 at 15:57
  • @Tommy saved you the trouble - see revised answer – Richard Hodges Sep 21 '16 at 16:14
  • 1
    I feel like that only proves probable safety — assuming the same eventually underlying realities as `realloc`, resizing does not necessarily imply movement. I guess we'd need also to check that `.data()` or `&myThings[0]` changes after insertion of the first thing and before insertion of the last? I apologise for being so unconstructive — I'm typing this from work and making slender justifications of use of time. I'm the one who thinks there might be a problem, please don't waste more of your time if you're certain not. I can prove myself wrong at my leisure (and promise to attempt to do so). – Tommy Sep 21 '16 at 17:31
  • Update: wrote a loop like `while(myThings.data() == initialValue) push_back` as an empirical test. Found no problems. Researched. Found http://clang.llvm.org/docs/AutomaticReferenceCounting.html#template-arguments (and the document generally). Clearly the compiler has been designed to be smart enough to apply proper ARC semantics to template arguments. The intelligence to get that right given the huge difference in implementation between ARC and `std::shared_ptr`/`weak_ptr` that's very impressive. And even if `std::vector` had been an issue, probably `std::list` would have been fine? – Tommy Sep 22 '16 at 13:47
  • @Tommy yes, since a list's nodes (and a map's) are guaranteed not to be invalidated during insertions etc. I think it's quite easy to implement from a compilers point of view. Anything that's castable to NSObject* is simply a special type, and not a "pointer". – Richard Hodges Sep 22 '16 at 17:01
13

If you do not require a specific order you could use NSMapTable with special key/value options

NSPointerFunctionsWeakMemory

Uses weak read and write barriers appropriate for ARC or GC. Using NSPointerFunctionsWeakMemory object references will turn to NULL on last release.

Erik Aigner
  • 28,109
  • 21
  • 129
  • 187
11

I believe the best solution for this is to use NSHashTable or NSMapTable. the Key or/and the Value can be weak. You can read more about it here: http://nshipster.com/nshashtable-and-nsmaptable/

Yaniv De Ridder
  • 763
  • 1
  • 7
  • 13
4

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 iOS 4.x too.

ArtFeel
  • 11,322
  • 4
  • 26
  • 41
  • 2
    Not clear on what makes any of these hold weak objects. – Graham Perks Dec 11 '14 at 20:22
  • Nil allocator. Look at declaration of this methods `CFMutableArrayRef CFArrayCreateMutable ( CFAllocatorRef allocator, CFIndex capacity, const CFArrayCallBacks *callBacks );` You can read more in documentation. – ArtFeel Dec 19 '14 at 13:31
  • 2
    The question asks for `__unsafe_unretained` and this correctly supplies `__unsafe_unretained`; it'd be nice if you didn't follow the original author's misuse of the term `weak` though. – Tommy Apr 24 '15 at 15:13
4

To add weak self reference to NSMutableArray, create a custom class with a weak property as given 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 delegateWeakReference == nil, the object can be removed from the array

The property will be nil, and the references will be deallocated at proper time independent of this array references

MistyD
  • 13,289
  • 28
  • 108
  • 198
  • Hi MistyD, please let me know why did you edit this post ? I didn't see any change made. Please don't edit my post – Harish Kumar Kailas Mar 31 '15 at 06:04
  • You can click on the "edited" line to see what was changed. Looking at the edit, I see that Misty added formatting to your code blocks so that they show up as code, none of your content was changed. This is a perfectly valid edit for someone to make and you shouldn't expect others to not edit your posts. – John Stephen Aug 01 '15 at 21:46
  • @HarishKumarKailas They were formatting your code into a code block, you had posted it as plain-text which is discouraged. – Albert Renshaw Jun 01 '20 at 07:14
3

No, that's not correct. Those aren't actually weak references. You can't really store weak references in an array right now. You need to have a mutable array and remove the references when you're done with them or remove the whole array when you're done with it, or roll your own data structure that supports it.

Hopefully this is something that they'll address in the near future (a weak version of NSArray).

Jason Coco
  • 76,627
  • 20
  • 180
  • 178
  • What if I wrap an `__unsafe_unretained` pointer to `Foo` inside another object that can be retained by the `NSArray`? – Emile Cormier Feb 17 '12 at 22:30
  • @EmileCormier You could do that, but you should make it a weak property, not `__unsafe_unretained`. That will prevent a retain cycle but you could very easily end up accessing garbage. – Jason Coco Feb 17 '12 at 22:32
  • I wish I could use a weak property, but I can't ignore the iOS 4.x market for my app. My background is C/C++, so I'm used to playing with fire in bad old days before `shared_ptr`. :-) – Emile Cormier Feb 17 '12 at 22:36
  • Depending on when your app is coming out, you may actually want to ignore iOS 4! :) If it follows the same trend as iOS 4, 6 months after launch iOS 5 should be in use in around 90% of the devices, and that mark is in April, so the extra work to support iOS 4 may not pay for the time spent coding/debugging for it. – Fernando Madruga Feb 17 '12 at 23:01
  • @FernandoMadruga: I saw some statistics in Jan 2012 that put iOS 5 adoption at around 66%. My app is almost ready for review, so the extra work for iOS 4 is mostly already done. Thanks for the tip anyway! – Emile Cormier Feb 17 '12 at 23:07
  • In that case it definitely pays... :) Good luck with your app. – Fernando Madruga Feb 17 '12 at 23:43
  • 2
    Just had an idea... Instead of using `__unsafe_unretained`, I can use a macro that expands to either `__unsafe_unretained` or `__weak`, depending on base SDK version. That way, I can detect dangling pointer problems during development on my iOS 5 device. – Emile Cormier Feb 17 '12 at 23:55
2

I've just faced with same problem and found that my before-ARC solution works after 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,
    &notRetainedCallbacks);
    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 00:59:10.266 not_retained_collection_test[28997:907] constructed 2013-06-30 00:59:10.267 not_retained_collection_test[28997:907] addet to set 2013-06-30 00:59:10.581 not_retained_collection_test[28997:907] constructed 2013-06-30 00:59:10.582 not_retained_collection_test[28997:907] addet to set 2013-06-30 00:59:10.881 not_retained_collection_test[28997:907] constructed 2013-06-30 00:59:10.882 not_retained_collection_test[28997:907] addet to set 2013-06-30 00:59:10.883 not_retained_collection_test[28997:907] deallocated 2013-06-30 00:59:10.883 not_retained_collection_test[28997:907] deallocated 2013-06-30 00:59:10.884 not_retained_collection_test[28997:907] deallocated 2013-06-30 00:59:10.885 not_retained_collection_test[28997:907] * -[TestObj respondsToSelector:]: message sent to deallocated instance 0x1f03c8c0

Checked with iOS versions 4.3, 5.1, 6.2. Hope it will be useful to somebody.

eug
  • 66
  • 3
1

If you need zeroing weak references, see this answer for code you can use for a wrapper class.

Other answers to that question suggest a block-based wrapper, and ways to automatically remove zeroed elements from the collection.

Community
  • 1
  • 1
paulmelnikow
  • 16,036
  • 6
  • 56
  • 110
1

If you use a lot this comportment it's indicated to your own NSMutableArray class (subclass of NSMutableArray) which doesn't increase the retain count.

You should have something 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;
}
Ciprian C
  • 247
  • 1
  • 6
-2

I think an elegant solution is what Mr. Erik Ralston propose on his Github repository

https://gist.github.com/eralston/8010285

this are the essential steps:

create a category for NSArray and NSMutableArray

in the implementation create a convenience class with a weak property. Your category will assign the objects to this weak property.

.h

 #import <Foundation/Foundation.h>

@interface NSArray(WeakArray)

- (__weak id)weakObjectForIndex:(NSUInteger)index;
-(id<NSFastEnumeration>)weakObjectsEnumerator;

@end

@interface NSMutableArray (FRSWeakArray)

-(void)addWeakObject:(id)object;
-(void)removeWeakObject:(id)object;

-(void)cleanWeakObjects;

@end

.m

#import "NSArray+WeakArray.h"

@interface WAArrayWeakPointer : NSObject
@property (nonatomic, weak) NSObject *object;
@end

@implementation WAArrayWeakPointer
@end

@implementation NSArray (WeakArray)


-(__weak id)weakObjectForIndex:(NSUInteger)index
{
    WAArrayWeakPointer *ptr = [self objectAtIndex:index];
    return ptr.object;
}

-(WAArrayWeakPointer *)weakPointerForObject:(id)object
{
    for (WAArrayWeakPointer *ptr in self) {
        if(ptr) {
            if(ptr.object == object) {
                return ptr;
            }
        }
    }

    return nil;
}

-(id<NSFastEnumeration>)weakObjectsEnumerator
{
    NSMutableArray *enumerator = [[NSMutableArray alloc] init];
    for (WAArrayWeakPointer *ptr in self) {
        if(ptr && ptr.object) {
            [enumerator addObject:ptr.object];
        }
    }
    return enumerator;
}

@end

@implementation NSMutableArray (FRSWeakArray)

-(void)addWeakObject:(id)object
{
    if(!object)
        return;

    WAArrayWeakPointer *ptr = [[WAArrayWeakPointer alloc] init];
    ptr.object = object;
    [self addObject:ptr];

    [self cleanWeakObjects];
}

-(void)removeWeakObject:(id)object
{
    if(!object)
        return;

    WAArrayWeakPointer *ptr = [self weakPointerForObject:object];

    if(ptr) {

        [self removeObject:ptr];

        [self cleanWeakObjects];
    }
}

-(void)cleanWeakObjects
{

    NSMutableArray *toBeRemoved = [[NSMutableArray alloc] init];
    for (WAArrayWeakPointer *ptr in self) {
        if(ptr && !ptr.object) {
            [toBeRemoved addObject:ptr];
        }
    }

    for(WAArrayWeakPointer *ptr in toBeRemoved) {
        [self removeObject:ptr];
    }
}

@end
Enrico Cupellini
  • 407
  • 7
  • 13
  • While this link may answer the question, it is better to include the essential parts of the answer here and provide the link for reference. Link-only answers can become invalid if the linked page changes. - [From Review](/review/low-quality-posts/17839903) – Nigel Ren Nov 04 '17 at 19:49
  • added code to the answer, now can you explain what's wrong with the solution in order to be down voted? – Enrico Cupellini Nov 05 '17 at 06:57
  • It may have been downvoted due to lack of the code, but unfortunately whoever downvoted you didn't bother to say anything (which I find annoying as well). – Nigel Ren Nov 05 '17 at 07:42