블록으로 '셀프' 사이클 유지
이 질문은 매우 기본적인 것이지만 블록에 들어가는 많은 Objective-C 프로그래머와 관련이 있다고 생각합니다.
바로는 은 그 에 블록은 변수를 캡처합니다.const
,, " "self
해당 블록을 복사할 경우 유지 사이클이 발생할 수 있습니다. 이렇게 해서 '우리에게 필요한 것 같다'를 하게 됩니다.__block
(을) self
복사하는 대신.
__block typeof(self) bself = self;
[someObject messageWithBlock:^{ [bself doSomething]; }];
뿐만 아니라
[someObject messageWithBlock:^{ [self doSomething]; }];
제가 알고 싶은 것은 다음과 같습니다.이것이 사실이라면 (GC를 사용하는 것 이외에) 추함을 피할 수 있는 방법이 있을까요?
엄밀히 말하면, 그것이 항상 복사라는 것은 이 문제와 무관합니다.obj-c를 사용하다문제의 const-copy의 retain을 합니다.__block
이치노
어쨌든, 당신의 질문에 대답하자면, 여기엔 진짜 대안이 없어요. API를 하는 타당하다면 값을 할 수 .self
의 논쟁으로서.안타깝게도 대부분의 API에서는 이 방법이 타당하지 않습니다.
이바리가 있는 는, 하거나, 「ivar」를 사용합니다.bself->ivar
.
할 때 ARC로 컴파일 할 때__block
더 이상 브레이크 유지 사이클이 없습니다. 할 ARC를 사용해야 .__weak
""__unsafe_unretained
★★★★★★ 。
사용방법:
__weak id weakSelf = self;
[someObject someMethodWithBlock:^{
[weakSelf someOtherMethod];
}];
자세한 내용은 WWDC 2011 - Blocks and Grand Central Dispatch in Practice.
https://developer.apple.com/videos/wwdc/2011/ ?id = 308
주의: 그래도 문제가 해결되지 않으면 시도해 보십시오.
__weak typeof(self)weakSelf = self;
이지만, 만 하면 .self
유지 주기를 갖게 될 거라는 걸 알고 있을 때 가명을 써요.만약 블록이 단발일 뿐이라면, 리테이너는 무시해도 된다고 생각합니다.self
나쁜 경우는 예를 들어 블록을 콜백인터페이스로 사용하는 경우입니다.다음과 같이 합니다.
typedef void (^BufferCallback)(FullBuffer* buffer);
@interface AudioProcessor : NSObject {…}
@property(copy) BufferCallback bufferHandler;
@end
@implementation AudioProcessor
- (id) init {
…
[self setBufferCallback:^(FullBuffer* buffer) {
[self whatever];
}];
…
}
여기서 API는 별로 의미가 없지만 예를 들어 슈퍼클래스와 통신할 때는 의미가 있습니다.우리는 버퍼 핸들러를 유지하고, 버퍼 핸들러는 우리를 유지합니다.다음과 같은 것과 비교해 보십시오.
typedef void (^Callback)(void);
@interface VideoEncoder : NSObject {…}
- (void) encodeVideoAndCall: (Callback) block;
@end
@interface Foo : NSObject {…}
@property(retain) VideoEncoder *encoder;
@end
@implementation Foo
- (void) somewhere {
[encoder encodeVideoAndCall:^{
[self doSomething];
}];
}
때는 안 self
하여 사이클이.유지 사이클은 발생하지만 동작은 단시간이며 결국 블록의 메모리가 부족해져 주기가 중단됩니다.하지만 블록에 대한 저의 경험은 매우 작아서 아마도self
에일리어스는 장기적으로 베스트 프랙티스로 나타납니다.
저도 문제여서 다른 답변을 올렸습니다.저는 원래 블록 내의 자기 참조가 있는 곳이라면 blockSelf를 사용해야 한다고 생각했습니다.이는 해당되지 않으며 객체 자체에 블록이 있는 경우에만 해당됩니다.실제로 이러한 경우 blockSelf를 사용하면 블록에서 결과를 가져오기 전에 객체가 할당 해제될 수 있으며, 객체를 호출하려고 하면 크래시되므로 응답이 반환될 때까지 자신을 유지할 필요가 있습니다.
첫 번째 경우는 블록에서 참조되는 블록을 포함하고 있기 때문에 유지 사이클이 발생하는 시기를 나타냅니다.
#import <Foundation/Foundation.h>
typedef void (^MyBlock)(void);
@interface ContainsBlock : NSObject
@property (nonatomic, copy) MyBlock block;
- (void)callblock;
@end
@implementation ContainsBlock
@synthesize block = _block;
- (id)init {
if ((self = [super init])) {
//__block ContainsBlock *blockSelf = self; // to fix use this.
self.block = ^{
NSLog(@"object is %@", self); // self retain cycle
};
}
return self;
}
- (void)dealloc {
self.block = nil;
NSLog (@"ContainsBlock"); // never called.
[super dealloc];
}
- (void)callblock {
self.block();
}
@end
int main() {
ContainsBlock *leaks = [[ContainsBlock alloc] init];
[leaks callblock];
[leaks release];
}
두 번째 경우에는 blockSelf가 필요하지 않습니다.이는 발신 객체에 self를 참조할 때 유지 사이클을 발생시키는 블록이 없기 때문입니다.
#import <Foundation/Foundation.h>
typedef void (^MyBlock)(void);
@interface BlockCallingObject : NSObject
@property (copy, nonatomic) MyBlock block;
@end
@implementation BlockCallingObject
@synthesize block = _block;
- (void)dealloc {
self.block = nil;
NSLog(@"BlockCallingObject dealloc");
[super dealloc];
}
- (void)callblock {
self.block();
}
@end
@interface ObjectCallingBlockCallingObject : NSObject
@end
@implementation ObjectCallingBlockCallingObject
- (void)doneblock {
NSLog(@"block call complete");
}
- (void)dealloc {
NSLog(@"ObjectCallingBlockCallingObject dealloc");
[super dealloc];
}
- (id)init {
if ((self = [super init])) {
BlockCallingObject *myobj = [[BlockCallingObject alloc] init];
myobj.block = ^() {
[self doneblock]; // block in different object than this object, no retain cycle
};
[myobj callblock];
[myobj release];
}
return self;
}
@end
int main() {
ObjectCallingBlockCallingObject *myObj = [[ObjectCallingBlockCallingObject alloc] init];
[myObj release];
return 0;
}
또한 블록이 다른 오브젝트를 참조하면 유지 사이클이 발생할 수 있습니다.self
.
가비지 컬렉션이 이러한 유지 사이클에 도움이 될지는 잘 모르겠습니다.오브젝트라고 가 유효기간을 초과한 self
('클라이언트 오브젝트'), 에 대한 self
블록 내부의 경우 고정 물체 자체가 해제될 때까지 순환으로 간주되지 않습니다.서버 객체가 클라이언트보다 오래 지속되면 메모리 누수가 심각할 수 있습니다.
클린 솔루션이 없기 때문에, 이하의 회피책을 추천합니다.그 중 하나 이상을 자유롭게 선택하여 문제를 해결하십시오.
- 블록은 완료 시에만 사용하고 오픈 엔드 이벤트에는 사용하지 마십시오.예를 들어, 다음과 같은 방법에는 블록을 사용합니다.
doSomethingAndWhenDoneExecuteThisBlock:
이런setNotificationHandlerBlock:
완성에 사용되는 블록은 수명이 정해져 있으므로 평가 후 서버 오브젝트에 의해 해제되어야 합니다.이렇게 하면 유지 주기가 발생하더라도 너무 오래 지속되는 것을 방지할 수 있습니다. - 네가 말한 약한 기준의 춤을 추렴
- 오브젝트가 해방되기 전에 오브젝트를 정리하는 메서드를 제공합니다.이 메서드는 오브젝트에 대한 참조를 보유할 가능성이 있는 서버 오브젝트에서 오브젝트를 "절단"하고 오브젝트에 대한 릴리스를 호출하기 전에 이 메서드를 호출합니다.이 메서드는 오브젝트에 클라이언트가1개만 있는 경우(또는 컨텍스트 내의 싱글톤인 경우)에는 문제가 없지만 여러 클라이언트가 있는 경우에는 문제가 없습니다.으로 물리치고 은, 「retain-counting mechanism」을 호출하는 .이것은 콜링과 비슷합니다.
dealloc
release
.
서버 개체를 쓰는 경우 완료를 위해서만 block 인수를 사용합니다.대한 예: "Da" "Block 인수"는.setEventHandlerBlock:
정식 "Delegate"를 setEventDelegate:
.츠키노정식 프로토콜을 작성하지 않으려면 셀렉터를 위임 콜백으로 수락하십시오.
마지막으로 다음 패턴이 알람을 울립니다.
- (표준) 할당 해제 {[myServerObject releaseCallbackBlocksForObject:self]; ...}
를 할 수 을 해제하려고 self
dealloc
넌 이미 곤경에 처했어 dealloc
는 블록 내의 참조에 의해 발생하는 유지 사이클 때문에 호출되지 않을 수 있습니다.즉, 서버 오브젝트의 할당이 해제될 때까지 오브젝트는 단순히 리크됩니다.
__block __unsafe_unretained
Kevin의 게시물에 제시된 수정자는 다른 스레드에서 실행되는 블록의 경우 잘못된 액세스 예외를 일으킬 수 있습니다.temp 변수에는 __block 한정자를 사용하고 사용 후에는 0으로 하는 것이 좋습니다.
__block SomeType* this = self;
[someObject messageWithBlock:^{
[this doSomething]; // here would be BAD_ACCESS in case of __unsafe_unretained with
// multithreading and self was already released
this = nil;
}];
libextopjc 라이브러리를 사용할 수 있습니다.예를 들어 Reactive Cocoa에서 사용되는 등 매우 인기가 있습니다.https://github.com/jspahrsummers/libextobjc
@weakify와 @strongify의 2개의 매크로를 제공하므로 다음과 같은 작업을 수행할 수 있습니다.
@weakify(self)
[someObject messageWithBlock:^{
@strongify(self)
[self doSomething];
}];
이것은 직접적인 강한 참조를 방해하기 때문에 우리는 자기 자신을 유지하는 사이클에 들어가지 않습니다.또, 자아가 도중에 영(0)이 되는 것을 막으면서도, 보유 카운트를 적절히 삭감합니다.자세한 것은, http://aceontech.com/objc/ios/2014/01/10/weakify-a-more-elegant-solution-to-weakself.html 를 참조해 주세요.
이건 어때?
- (void) foo {
__weak __block me = self;
myBlock = ^ {
[[me someProp] someMessage];
}
...
}
컴파일러 경고가 더 이상 표시되지 않습니다.
블록: 블록에서 참조되는 블록이 포함되어 있기 때문에 유지 사이클이 발생합니다.블록 복사를 하고 멤버 변수를 사용하면 유지 사이클이 자동으로 발생합니다.
언급URL : https://stackoverflow.com/questions/4352561/retain-cycle-on-self-with-blocks
'programing' 카테고리의 다른 글
Postgre에 작은 따옴표로 텍스트를 삽입합니다.SQL (0) | 2023.04.19 |
---|---|
Items Control을 가상화하시겠습니까? (0) | 2023.04.19 |
어레이/어레이 목록에서 링크된 목록을 사용하는 경우 (0) | 2023.04.19 |
ORA-01461: LONG 열에 삽입하는 경우에만 LONG 값을 바인딩할 수 있습니다. 쿼리 시 발생합니다. (0) | 2023.04.19 |
UITextView 플레이스 홀더 (0) | 2023.04.19 |