NSARC의 객체에 대한 약한 참조 (__unsafe_unretained) 배열
유지주기를 방지하기 위해 개체에 대한 약한 참조를 NSArray에 저장해야합니다. 사용할 적절한 구문을 잘 모르겠습니다. 이것이 올바른 방법입니까?
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];
나는 아이폰 OS 4.x의를 지원해야합니다 따라서, __unsafe_unretained대신 __weak.
수정 (2015-02-18) :
실제 __weak포인터 (아님 __unsafe_unretained) 를 사용하려는 사람들은 대신이 질문을 확인하십시오. ARC에서 약한 참조를 제로화하는 컬렉션
Jason이 말했듯이 NSArray스토어 약한 참조를 만들 수 없습니다 . 약한 참조를 저장하는 다른 개체 내부에 개체를 래핑하는 Emile의 제안을 구현하는 가장 쉬운 방법은 다음과 같습니다.
NSValue *value = [NSValue valueWithNonretainedObject:myObj];
[array addObject:value];
또 다른 옵션 : 선택적으로 약한 참조를 저장 하는 카테고리 입니다 NSMutableArray.
이들은 자체 제로화 약한 참조가 아니라 "안전하지 않은 유지되지 않은"참조입니다. 객체가 할당 해제 된 후에도 배열이 여전히 주변에 있으면 정크 포인터가 많이있을 것입니다.
NSValue 도우미 를 사용 하거나 컬렉션 (배열, 설정, dict) 개체를 만들고 해당 Retain / Release 콜백을 비활성화하는 솔루션은 모두 ARC 사용과 관련하여 100 % 안전하지 않은 솔루션이 아닙니다.
이러한 제안에 대한 다양한 의견이 지적했듯이 이러한 개체 참조는 진정한 약한 참조처럼 작동하지 않습니다.
ARC에서 지원하는 "적절한"약한 속성에는 두 가지 동작이 있습니다.
- 대상 개체에 대한 강력한 참조를 보유하지 않습니다. 즉, 객체에 자신을 가리키는 강력한 참조가 없으면 객체가 할당 해제됩니다.
- ref'd 객체가 할당 해제되면 약한 참조는 nil이됩니다.
이제 위의 솔루션은 동작 # 1을 준수하지만 # 2를 표시하지 않습니다.
동작 # 2도 얻으려면 자신의 헬퍼 클래스를 선언해야합니다. 참조를 유지하는 데 약한 속성이 하나뿐입니다. 그런 다음이 도우미 개체를 컬렉션에 추가합니다.
아, 그리고 한 가지 더 : iOS6 및 OSX 10.8이 더 나은 솔루션을 제공 할 것으로 보입니다.
[NSHashTable weakObjectsHashTable]
[NSPointerArray weakObjectsPointerArray]
[NSPointerArray pointerArrayWithOptions:]
이것들은 약한 참조를 보유하는 컨테이너를 제공 할 것입니다 (그러나 아래의 matt의 주석에 유의하십시오).
저는 20 년 동안 C ++를 작성한 후 Objective-C를 처음 접했습니다.
제 생각에, Objective-C는 느슨하게 결합 된 메시징에 탁월하지만 데이터 관리에는 끔찍합니다.
xcode 4.3이 Objective-C ++를 지원한다는 사실을 알게되어 얼마나 기뻤는지 상상해보십시오!
이제 모든 .m 파일의 이름을 .mm (objective-c ++로 컴파일)로 바꾸고 데이터 관리를 위해 C ++ 표준 컨테이너를 사용합니다.
따라서 "약한 포인터 배열"문제는 __weak 객체 포인터의 std :: vector가됩니다.
#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
}
}
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
}
}
개념 증명 (벡터 재 할당에 대한 Tommy의 우려에 비추어 볼 때) :
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]);
}
로그 출력 예 :
2016-09-21 18:11:13.150 foo2[42745:5048189] Thing
2016-09-21 18:11:13.152 foo2[42745:5048189] (null)
특정 주문이 필요하지 않은 경우 NSMapTable특수 키 / 값 옵션과 함께 사용할 수 있습니다.
NSPointerFunctionsWeakMemory
ARC 또는 GC에 적합한 약한 읽기 및 쓰기 장벽을 사용합니다. NSPointerFunctionsWeakMemory 개체 참조를 사용하면 마지막 릴리스에서 NULL이됩니다.
이에 대한 가장 좋은 해결책은 NSHashTable 또는 NSMapTable을 사용하는 것입니다. 키 또는 / 및 값이 약할 수 있습니다. 여기에서 자세한 내용을 읽을 수 있습니다. http://nshipster.com/nshashtable-and-nsmaptable/
NSMutableArray에 약한 자체 참조를 추가하려면 아래와 같이 약한 속성이있는 사용자 지정 클래스를 만듭니다.
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];
}
3 단계 : 나중에 속성 delegateWeakReference == nil인 경우 객체를 배열에서 제거 할 수 있습니다.
속성은 nil이고 참조는이 배열 참조와 관계없이 적절한 시간에 할당 해제됩니다.
가장 간단한 해결책 :
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);
참고 : iOS 4.x에서도 작동합니다.
아뇨, 정답이 아닙니다. 그것들은 실제로 약한 참조가 아닙니다. 지금은 약한 참조를 배열에 저장할 수 없습니다. 변경 가능한 배열이 있어야하며 참조가 끝나면 참조를 제거하거나 작업이 끝나면 전체 배열을 제거하거나이를 지원하는 자체 데이터 구조를 롤링해야합니다.
바라건대 이것은 그들이 가까운 장래에 다룰 것입니다 (의 약한 버전 NSArray).
나는 방금 같은 문제에 직면했고 ARC로 설계된대로 변환 한 후 이전 ARC 솔루션이 작동한다는 것을 알았습니다.
// 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
산출:
2013-06-30 00 : 59 : 10.266 not_retained_collection_test [28997 : 907] 생성됨 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] 생성됨 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] created 2013-06-30 00 : 59 : 10.882 not_retained_collection_test [28997 : 907] 설정에 대한 addet 2013-06-30 00 : 59 : 10.883 not_retained_collection_test [28997 : 907] 할당 해제 됨 2013-06-30 00 : 59 : 10.883 not_retained_collection_test [28997 : 907] 할당 해제 됨 2013-06-30 00:59 : 10.884 not_retained_collection_test [28997 : 907] 할당 해제 됨 2013-06-30 00 : 59 : 10.885 not_retained_collection_test [28997 : 907] * -[TestObj respondsToSelector :] : 할당 해제 된 인스턴스 0x1f03c8c0에 메시지 전송
iOS 버전 4.3, 5.1, 6.2로 확인했습니다. 누군가에게 유용하기를 바랍니다.
약한 참조를 제로화 해야하는 경우 래퍼 클래스에 사용할 수있는 코드에 대한 이 답변 을 참조하십시오 .
이 질문에 대한 다른 답변 은 블록 기반 래퍼와 컬렉션에서 제로화 요소를 자동으로 제거하는 방법을 제안합니다.
이 부분을 많이 사용하는 경우 보유 수를 늘리지 않는 자체 NSMutableArray 클래스 (NSMutableArray의 하위 클래스)에 표시됩니다.
다음과 같은 것이 있어야합니다.
-(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;
}
우아한 해결책은 Erik Ralston이 Github 저장소에서 제안한 것입니다.
https://gist.github.com/eralston/8010285
다음은 필수 단계입니다.
NSArray 및 NSMutableArray에 대한 카테고리 생성
구현에서 약한 속성으로 편의 클래스를 만듭니다. 카테고리는이 약한 속성에 개체를 할당합니다.
.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
.미디엄
#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
'Nice programing' 카테고리의 다른 글
| Python : 한 세트에 다른 세트가 완전히 포함되어 있는지 확인 하시겠습니까? (0) | 2020.11.07 |
|---|---|
| Git은 파일을 어디에 저장합니까? (0) | 2020.11.07 |
| C # 문자열 분할 및 결합 (0) | 2020.11.06 |
| Java의 arraylist에서 고유 값 가져 오기 (0) | 2020.11.06 |
| svg 배경 이미지 위치는 background-position : left center에도 불구하고 항상 Internet Explorer에서 중앙에 위치합니다. (0) | 2020.11.06 |