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
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.
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:
- nie trzyma silnego ref do obiektu docelowego. To znaczy że jeśli obiekt nie ma silnych odniesień wskazujących na niego, obiekt zostanie dealokowany.
- 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).
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)
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.
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/
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
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ż.
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
).
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,
¬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
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.
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.
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;
}
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
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