NSArray słabych odniesień (unsafe unreased) do obiektów pod łukiem

Muszę przechowywać słabe odniesienia do obiektów w NSArray, aby zapobiec zatrzymywaniu cykli. Nie jestem pewien, jakiej składni użyć. Czy to jest właściwa droga?

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];

Zauważ, że muszę obsługiwać iOS 4.x, czyli __unsafe_unretained zamiast __weak.


Edytuj (2015-02-18):

Dla tych, którzy chcą użyć true __weak pointers (nie __unsafe_unretained), proszę sprawdzić to pytanie zamiast: Kolekcje zerowania słabych odniesień pod ARC

Author: Community, 2012-02-18

12 answers

Jak powiedział Jason, nie możesz przechowywać słabych odniesień. Najprostszym sposobem na zaimplementowanie sugestii Emile ' a o zawijaniu obiektu wewnątrz innego obiektu, który przechowuje słabe odniesienie do niego, jest:

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

Inna opcja: Kategoria , która sprawia, że NSMutableArray opcjonalnie przechowuje słabe referencje.

Zauważ, że są to" niebezpieczne, nieotrzymane " odniesienia, a nie samoczynne zerowanie słabych odniesień. Jeśli tablica jest nadal w pobliżu po dealokacji obiektów, będziesz mieć kilka ze śmieciowych wskaźników.

 72
Author: yuji,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:34:59

Rozwiązania do użycia helpera NSValue lub do utworzenia obiektu collection (array, set, dict) i wyłączenia jego wywołań zwrotnych Zachowaj/Zwolnij nie są w 100% bezpiecznymi rozwiązaniami w odniesieniu do używania ARC.

Jak wskazują różne komentarze do tych sugestii, takie odniesienia do obiektów nie będą działać jak prawdziwe słabe refy:

"właściwa" słaba właściwość, wspierana przez ARC, ma dwa zachowania:

  1. nie trzyma silnego ref do obiektu docelowego. To znaczy że jeśli obiekt nie ma silnych odniesień wskazujących na niego, obiekt zostanie dealokowany.
  2. jeśli obiekt ref ' D jest dealokowany, słabe odniesienie stanie się zerowe.

Teraz, podczas gdy powyższe rozwiązania będą zgodne z zachowaniem #1, nie wykazują one #2.

Aby uzyskać zachowanie #2, musisz zadeklarować własną klasę pomocnika. Ma tylko jedną słabą właściwość do przechowywania referencji. Następnie dodajesz ten obiekt pomocniczy do kolekcji.

I jeszcze jedno. rzecz: iOS6 i OSX 10.8 podobno oferują lepsze rozwiązanie:
[NSHashTable weakObjectsHashTable]
[NSPointerArray weakObjectsPointerArray]
[NSPointerArray pointerArrayWithOptions:]

Powinny dać ci kontenery, które zawierają słabe odniesienia (ale zwróć uwagę na komentarze Matta poniżej).

 54
Author: Thomas Tempelmann,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:18:34

Jestem nowy w objective-C, po 20 latach pisania c++.

Moim zdaniem objective-C jest znakomity w luźno powiązanych komunikatach, ale fatalny w zarządzaniu danymi.

Wyobraź sobie, jak szczęśliwy byłem, aby odkryć, że xcode 4.3 obsługuje objective-c++!

Więc teraz zmieniam nazwę wszystkich moich .pliki m Do. mm (kompiluje jako objective-c++) i używać standardowych kontenerów c++ do zarządzania danymi.

W ten sposób problem "array of weak pointers" staje się std:: vector of _ _ weak object wskaźniki:

#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
  }
}

, który jest odpowiednikiem idiomu c++:

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
  }
}

Dowód koncepcji (w świetle obaw Tommy ' ego o realokację wektorów):

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]);
}

Przykładowe wyjście dziennika:

2016-09-21 18:11:13.150 foo2[42745:5048189] Thing
2016-09-21 18:11:13.152 foo2[42745:5048189] (null)
 24
Author: Richard Hodges,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-09-21 16:13:48

Jeśli nie potrzebujesz konkretnego zamówienia, możesz użyć NSMapTable ze specjalnymi opcjami klucza / wartości

NSPointerFunctionsWeakMemory

Używa słabych barier odczytu i zapisu odpowiednich dla ARC lub GC. Używanie odniesień do obiektów NSPointerFunctionsWeakMemory zmieni wartość NULL w ostatnim wydaniu.

 12
Author: Erik Aigner,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-04-04 07:51:32

Uważam, że najlepszym rozwiązaniem jest użycie NSHashTable lub NSMapTable. klucz lub / i wartość mogą być słabe. Możesz przeczytać więcej na ten temat tutaj: http://nshipster.com/nshashtable-and-nsmaptable/

 9
Author: Yaniv De Ridder,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-11-20 20:49:51

Aby dodać słabe samo odniesienie do NSMutableArray, utwórz własną klasę ze słabą właściwością, jak podano poniżej.

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];

}

Krok 3: później, jeśli właściwość delegateWeakReference == nil, obiekt może zostać usunięty z tablicy

Właściwość będzie zerowa, a referencje będą dealokowane we właściwym czasie niezależnie od referencji tablicy

 4
Author: Harish Kumar Kailas,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-03-30 20:47:10

Najprostsze rozwiązanie:

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);

Uwaga: i to działa na iOS 4.x też.

 4
Author: ArtFeel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-04-29 13:51:08

Nie, to nieprawda. To nie są słabe referencje. Nie możesz teraz przechowywać słabych odniesień w tablicy. Musisz mieć zmienną tablicę i usunąć referencje, gdy skończysz z nimi lub usunąć całą tablicę, gdy skończysz z nią, lub zwiń własną strukturę danych, która ją obsługuje.

Miejmy nadzieję, że jest to coś, co zajmą się w najbliższej przyszłości(słaba Wersja NSArray).

 3
Author: Jason Coco,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-02-17 22:26:49

Właśnie spotkałem się z tym samym problemem i okazało się, że moje rozwiązanie przed-ARC działa po konwersji z ARC zgodnie z projektem.

// 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

Wyjście:

2013-06-30 00:59:10.266 not_retained_collection_test[28997: 907] skonstruowany 2013-06-30 00:59: 10.267 not_retained_collection_test [28997:907] addet to zestaw 2013-06-30 00:59:10.581 not_retained_collection_test[28997: 907] skonstruowany 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] skonstruowany 2013-06-30 00: 59: 10.882 not_retained_collection_test [28997:907] addet to zestaw 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:]: Wiadomość wysłana do zdalokowanej instancji 0x1f03c8c0

Sprawdzono w wersjach iOS 4.3, 5.1, 6.2. Mam nadzieję, że komuś się przyda.

 2
Author: eug,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-06-29 22:33:58

Jeśli potrzebujesz zerowania słabych odniesień, Zobacz ta odpowiedź dla kodu, którego możesz użyć dla klasy wrapper.

Inne odpowiedzi na to pytanie sugerują opakowanie oparte na blokach i sposoby automatycznego usuwania zerowanych elementów z kolekcji.

 1
Author: paulmelnikow,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:33:25

Jeśli używasz dużo tego comportmentu, jest on wskazany do Twojej własnej klasy NSMutableArray (podklasa NSMutableArray), która nie zwiększa liczby zatrzymań.

Powinieneś mieć coś takiego:

-(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;
}
 1
Author: Ciprian C,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-11-05 22:30:21

Myślę, że eleganckie rozwiązanie proponuje Pan Erik Ralston na repozytorium Github.]}

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

Oto podstawowe kroki:

Utwórz kategorię dla NSArray i NSMutableArray

W implementacji Utwórz klasę wygody ze słabą właściwością. Twoja Kategoria przydzieli obiekty do tej słabej właściwości.

.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
 -2
Author: Enrico Cupellini,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-11-04 22:11:29