ARC에서 IBOutlet이 강하거나 약해야합니까?


551

ARC를 사용하여 iOS 5 전용으로 개발 중입니다. 한다은 IBOutlet에이야 UIView의 (및 서브 클래스) 일 strong이나 weak?

다음과 같은:

@property (nonatomic, weak) IBOutlet UIButton *button;

이 모든 것을 제거 할 것입니다 :

- (void)viewDidUnload
{
    // ...
    self.button = nil;
    // ...
}

이 작업에 문제가 있습니까? 템플릿은 strong'Interface Builder'편집기에서 헤더에 직접 연결할 때 자동으로 생성 된 속성 을 그대로 사용 하지만 왜 그럴까요? 은 UIViewController이미 가지고 strong그 참조 view그 파단을 유지한다.


11
참고로 IBOutletCollection()해서는 안되며 weak그렇지 않으면로 반환됩니다 nil.
ohho

Xcode 8.2.1은 인터페이스 빌더를 통해 IBOutlets를 생성 할 때 weak를 사용합니다. 그러나 여기에 많은 답변이 강력하게 사용하는 것이 좋습니다.
neoneye

1
@neoneye 방금 스토리 보드에서 빠른 파일로 xcode 8.3.2 드래그로 시도했으며 기본값은strong
CupawnTae

답변:


252

Apple에서 현재 권장하는 모범 사례 는 유지주기를 피하기 위해 특별히 약한 것이 필요 하지 않은 한 IBOutlets를 강력하게 하는 것입니다. Johannes가 위에서 언급했듯이, 이는 Apple Engineer가 WWDC 2015의 "Interface Builder에서 UI 디자인 구현"세션에서 언급 한 내용입니다.

마지막으로 지적하고자하는 옵션은 강력하거나 약한 스토리지 유형입니다. 일반적으로 콘센트를 하위 뷰에 연결하거나 항상보기 계층 구조에 의해 유지되지 않는 구속 조건에 연결하는 경우 콘센트를 강하게 만들어야합니다. 실제로 아울렛을 약화시켜야하는 유일한 시점은보기 계층 구조를 백업하는 항목을 참조하는 사용자 정의보기가 있고 일반적으로 권장하지 않는 경우입니다.

나는 트위터에서 IB 팀의 엔지니어에게 이것에 대해 물었고 그는 기본값 이 강력 해야하며 개발자 문서가 업데이트되고 있음을 확인했습니다 .

https://twitter.com/_danielhall/status/620716996326350848 https://twitter.com/_danielhall/status/620717252216623104


33
이것이 사실입니까 아니면 300+ 이상의 답이 올바른 답입니까? 스토리 보드에서 .h로 Ctrl- 드래그 할 때 InterfaceBuilder는 기본적으로 약함을 사용하는 것으로 나타났습니다.
Arunabh Das

4
400 개 이상의 투표권을 가진 사람은 정확하지만 구식입니다. iOS 6 viewDidUnload가 호출되지 않기 때문에 콘센트가 약하다는 이점이 없습니다.
kjam

7
@ kjam 혜택이 있습니다. 무엇보다도 당신은 당신이 만들지 않은 것에 대해 강한 언급을해서는 안됩니다. 둘째, 성능 향상은 무시할만한 수준입니다. 잘 배치 된 사람조차도 10 마이크로 초 더 빠르다고 말했기 때문에 프로그래밍의 모범 사례를 위반하지 마십시오. 명확한 의도, 최적화 컴파일러를 재생하려고하지 마십시오. 특정 경우에 문제가되는 것으로 측정 된 경우에만 성능 코드.
Cameron Lowell Palmer

5
동의하지 않겠습니다. Objective-C에서 '내가 만들지 않은 것에 대한 강한 참조를 유지하는 것'은 항상 일어난다. 그렇기 때문에 단일 소유자가 아닌 참조 횟수가 있습니다. 이 권장 사항을 백업하는 데 참조가 있습니까? 당신은 pls 약한 콘센트의 다른 장점을 나열 할 수 있습니까?
kjam

4
여기 답변에 언급 된 WWDC 비디오입니다 developer.apple.com/videos/play/wwdc2015/407/?time=1946
petrsyn

450

경고, 오래된 답변 :이 답변은 WWDC 2015에 따른 최신 정보가 아닙니다. 정답은 위의 수락 된 답변 (Daniel Hall)을 참조하십시오. 이 답변은 기록을 유지합니다.


개발자 라이브러리 에서 요약 :

실용적인 관점에서, iOS 및 OS X 콘센트는 선언 된 속성으로 정의해야합니다. 아웃렛은 일반적으로 파일 소유자에서 nib 파일 (또는 iOS의 경우 스토리 보드 장면)의 최상위 레벨 오브젝트에 이르는 것을 제외하고는 약해야합니다. 따라서 생성하는 콘센트는 일반적으로 다음과 같은 이유로 기본적으로 약합니다.

  • 예를 들어, 뷰 컨트롤러보기 또는 창 컨트롤러 창의 하위보기에 대해 생성하는 아울렛은 소유권을 의미하지 않는 개체 간의 임의 참조입니다.

  • 강력한 콘센트는 프레임 워크 클래스 (예 : UIViewController의보기 콘센트 또는 NSWindowController의 창 콘센트)에 의해 지정됩니다.

    @property (weak) IBOutlet MyView *viewContainerSubview;
    @property (strong) IBOutlet MyOtherClass *topLevelObject;

10
애플 문서 페이지의 특정 부분으로 이동하기 위해 "개발자 라이브러리"링크를 어떻게 얻었습니까? Apple 문서에 연결할 때마다 항상 관심있는 내용이 페이지의 절반 아래에 있더라도 항상 페이지 상단에 연결됩니다. 감사.
bearMountain

68
왼쪽의 탐색 창에서 링크를 복사했습니다. : D
Alexsander Akers

27
"File 's Owner에서 nib 파일 (또는 iOS의 경우 스토리 보드 장면)의 최상위 개체까지의 개체 제외"란 무엇입니까?
Van Du Tran

16
@VanDuTran-루트 레벨에있는 NIB의 객체를 의미합니다. 즉, 메인 뷰의 하위 뷰가 아닌 다른 뷰를 인스턴스화 한 경우 강력한 참조가 필요합니다.
mattjgalloway

6
최상위 수준은 펜촉을 볼 때 개체가 왼쪽의 목록에 나타납니다. 거의 모든 펜촉에 UIView가 있습니다. 이것이 유일한 최상위 개체 일 수 있습니다. 다른 항목을 추가하고 목록에 표시하면 "최상위 개체"입니다
David H

50

이 문서에서는 weak하위 뷰의 속성 사용 을 권장하지만 iOS 6부터는 strong대신 기본 소유권 한정자 를 사용 하는 것이 좋습니다. 이는 UIViewController뷰 의 변경이 더 이상 언로드되지 않기 때문에 발생합니다 .

  • iOS 6 이전에는 컨트롤러 뷰의 하위 뷰에 대한 강력한 링크를 유지 한 경우 뷰 컨트롤러의 메인 뷰가 언로드되면 뷰 컨트롤러가 주변에있는 한 서브 뷰에 유지됩니다.
  • iOS 6부터는 뷰가 더 이상 언로드되지 않고 한 번로드 된 다음 컨트롤러가있는 한 고정됩니다. 따라서 강한 속성은 중요하지 않습니다. 또한 강한 참조 그래프를 가리 키기 때문에 강한 참조주기를 생성하지 않습니다.

즉, 나는 사용하는 사이에 찢어진

@property (nonatomic, weak) IBOutlet UIButton *button;

@property (nonatomic) IBOutlet UIButton *button;

iOS 6 이상에서 :

  • 사용 weak하면 컨트롤러가 버튼의 소유권을 원하지 않음이 명확하게 나타납니다.

  • 그러나 생략하면 weak뷰 언로드없이 iOS 6에서 아프지 않으며 더 짧습니다. 일부는 더 빠를 수도 있지만 weak IBOutlets로 인해 너무 느린 앱을 아직 만나지 못했습니다 .

  • 사용하지 않으면 weak오류로 인식 될 수 있습니다.

결론 : iOS 6부터 뷰 언로드를 사용하지 않는 한 더 이상 잘못 얻을 수 없습니다. 파티 시간. ;)


사실이지만 여전히 뷰를 언로드하고 싶을 수도 있습니다. 이 경우 모든 콘센트를 nil수동으로 설정 해야합니다.
hypercrypt

추신 : weakARM64에 상당히 저렴합니다 : D
hypercrypt

뷰 언로드를 구현하면 weak속성 또는 __weak인스턴스 변수가 좋습니다. 나는 여기서 오류 가능성이 적다는 것을 지적하고 싶었습니다. 에 관해서는 weakarm64에 저렴되고, 난과 실제 성능 문제를 보지 못했어요 weak IBOutlet하는 ARMv7에들. :)
Tammo Freese

이 경우 strong에도 의미가 있습니다. strong뷰 언로드를 사용하는 경우에만 유해하지만 요즘 누가합니까? :)
Tammo Freese

2
@Rocotilos 최초의 iPhone은 매우 제한된 RAM을 가지고있었습니다. 128MB를 올바르게 호출하면 활성 앱에 약 10MB가 남습니다. 작은 메모리 공간을 확보하는 것이 중요했기 때문에 뷰 언로드가 발생했습니다. 이제는 점점 더 많은 RAM이 있고 iOS 6에서 Apple이 UIView를 최적화하여 메모리 경고시 뷰를 언로드하지 않고도 많은 메모리를 확보 할 수있게되었습니다.
Tammo Freese

34

나는 그것에 아무런 문제가 보이지 않습니다. 사전 아크, 나는 항상 assign그들의 IBOutlets를 만들었습니다 . 왜냐하면 그들은 이미 슈퍼 뷰에 의해 유지되기 때문입니다. 당신이 그 (것)을하면 weak, 당신은 당신이 지적으로, viewDidUnload에서 그들을 nil을 할 필요가 없습니다.

한 가지주의 사항 : ARC 프로젝트에서 iOS 4.x를 지원할 수 있지만 weak그렇게하면 사용할 수 없으므로을 사용해야 합니다 assign.이 경우 viewDidUnload피하기 위해 참조를 생략하고 싶습니다 매달려있는 포인터. 다음은 내가 경험 한 매달려있는 포인터 버그의 예입니다.

UIViewController에는 우편 번호를위한 UITextField가 있습니다. CLLocationManager를 사용하여 사용자 위치를 역 지오 코딩하고 우편 번호를 설정합니다. 델리게이트 콜백은 다음과 같습니다.

-(void)locationManager:(CLLocationManager *)manager
   didUpdateToLocation:(CLLocation *)newLocation
          fromLocation:(CLLocation *)oldLocation {
    Class geocoderClass = NSClassFromString(@"CLGeocoder");
    if (geocoderClass && IsEmpty(self.zip.text)) {
        id geocoder = [[geocoderClass alloc] init];
        [geocoder reverseGeocodeLocation:newLocation completionHandler:^(NSArray *placemarks, NSError *error) {
            if (self.zip && IsEmpty(self.zip.text)) {
                self.zip.text = [[placemarks objectAtIndex:0] postalCode];
            }
        }];    
    }
    [self.locationManager stopUpdatingLocation];
}

적절한 시점 에이보기를 닫고 self.zip을 생략하지 않으면 viewDidUnload대리자 콜백이 self.zip.text에서 잘못된 액세스 예외를 던질 수 있음을 발견했습니다.


4
또한 weak속성이 필요하지 않다는 것을 이해하고 있습니다 viewDidUnload. 그러나 아울렛을 만들기위한 Apple의 템플릿에는 왜 [self setMySubview:nil]?
Yang Meyer

3
IBOutlet에 강력 / 보강을 사용하면 문제가 발생할 수있는 실제 사례가 있습니까? 아니면 중복 보존입니까, 코딩 스타일이 좋지 않지만 코드에 영향을 미치지 않습니까?
Enzo Tran

1
중복 보유와 같은 것이 있습니까? 추가 보유가있는 경우 올바르게 계산되지 않으므로 보유 계수에 추가 보유가 있기 때문에 가능한 빨리 해제되지 않습니다.
karlbecker_com 1

25

IBOutlet성능상의 이유로 강력해야합니다. 참조 스토리 보드 참조, 강한 함께 IBOutlet, 아이폰 OS의 장면 독 (9)

이 단락에서 설명했듯이 뷰 컨트롤러 뷰의 하위 뷰에 대한 출구는 약한 것일 수 있습니다. 이러한 하위 뷰는 nib 파일의 최상위 개체가 이미 소유하고 있기 때문입니다. 그러나 Outlet이 약한 포인터로 정의되고 포인터가 설정된 경우 ARC는 런타임 함수를 호출합니다.

id objc_storeWeak(id *object, id value);

그러면 객체 값을 키로 사용하여 포인터 (객체)를 테이블에 추가합니다. 이 테이블을 약한 테이블이라고합니다. ARC는이 테이블을 사용하여 응용 프로그램의 모든 약한 포인터를 저장합니다. 이제 객체 값이 할당 해제되면 ARC는 약한 테이블을 반복하고 약한 참조를 nil로 설정합니다. 또는 ARC는 다음을 호출 할 수 있습니다.

void objc_destroyWeak(id * object)

그런 다음 객체가 등록 해제되고 objc_destroyWeak가 다시 호출합니다.

objc_storeWeak(id *object, nil)

약한 참조와 관련된이 책 유지는 강한 참조의 릴리스보다 2-3 배 더 오래 걸릴 수 있습니다. 따라서 약한 참조는 단순히 콘센트를 강력하게 정의하여 피할 수있는 런타임 오버 헤드를 발생시킵니다.

Xcode 7부터는 제안합니다. strong

인터페이스 빌더에서 WWDC 2015 세션 407 UI 디자인 구현 을 볼 경우 ( http://asciiwwdc.com/2015/sessions/407의 스크립트 )

마지막으로 지적하고자하는 옵션은 강력하거나 약한 스토리지 유형입니다.

일반적으로 콘센트를 하위보기 또는 항상보기 계층 구조에 의해 유지되지 않는 구속 조건에 연결하는 경우 콘센트를 강하게 만들어야합니다.

실제로 아울렛을 약화시켜야하는 유일한 시점은보기 계층 구조를 백업하는 항목을 참조하는 사용자 정의보기가 있고 일반적으로 권장하지 않는 경우입니다.

강력하게 선택하고 연결을 클릭하여 콘센트를 생성합니다.


1
실제 이유를 설명하는 훌륭한 답변 -why-
micnguyen

그것은 훌륭하지만 모두 스토리 보드에 구현 된 제스처 인식기에서 누출이 발생하는 것을 보았습니다.
thibaut noah

1
이 줄을 이해할 수 없습니다. "아웃렛을 약하게 만들어야하는 유일한 시점은 뷰 계층 구조를 백업하는 무언가를 참조하는 사용자 정의 뷰가 있고 일반적으로 권장되지 않는 경우입니다." 예가 있습니까?
user1872384

약하고 강한 데 걸리는 초기화 시간을 계산했는데 정확히 같습니다.
touti

그러나 신속하게 이것은 더 많은 경우입니다. 약한 참조가 더 빠릅니다.
thesummersign

20

iOS 개발에서 NIB 로딩은 Mac 개발과 약간 다릅니다.

Mac 개발에서 IBOutlet은 일반적으로 약한 참조입니다. NSViewController의 서브 클래스가있는 경우 최상위 뷰만 유지되며 컨트롤러 할당을 해제하면 모든 서브 뷰와 콘센트가 자동으로 해제됩니다.

UiViewController는 Key Value Coding을 사용하여 강력한 참조를 사용하여 콘센트를 설정합니다. 따라서 UIViewController를 할당 해제하면 상위 뷰가 자동으로 할당 해제되지만 dealloc 메소드에서 모든 콘센트를 할당 해제해야합니다.

Big Nerd Ranch의이 글 에서이 주제를 다루고 IBOutlet에서 강력한 참조를 사용하는 것이 왜 좋은 선택이 아닌지 설명합니다 (이 경우 Apple에서 권장하더라도).


16
2009 년에 설명되어 있습니다. ARC에서는 크게 바뀌 었습니다.
Dafydd Williams

1
: (Big Nerd Ranch 링크는 죽었습니다… 아직 읽어야합니다. 누구나 그 게시물에 대한 자세한 내용을 알고 있으므로 찾을 수 있습니까?
Motti Shneor

@MottiShneor는 걱정하지 마십시오. 링크가 ARC 이전에 있었기 때문에 더 이상 중요하지 않으므로 더 이상 관련이 없습니다.
Sergey Grischyov

18

여기서 지적하고 싶은 것은 애플 엔지니어들이 WWDC 2015 비디오에서 언급 한 내용에도 불구하고

https://developer.apple.com/videos/play/wwdc2015/407/

애플은이 주제에 대한 생각을 계속 바꾸어이 질문에 대한 정답이 하나도 없다고 말합니다. Apple 엔지니어조차도이 주제에 대해 나뉘어져 있음을 보여주기 위해 Apple의 최신 샘플 코드를 살펴보면 일부 사람들은 약한 것을 사용하고 그렇지 않은 사람들도 있습니다.

이 Apple Pay 예제는 약한 것을 사용합니다. https://developer.apple.com/library/ios/samplecode/Emporium/Listings/Emporium_ProductTableViewController_swift.html#//apple_ref/doc/uid/TP40016175-Emporium_ProductTableViewController_swift-DontLinkElementID_8

이 picture-in-picture 예와 마찬가지로 https://developer.apple.com/library/ios/samplecode/AVFoundationPiPPlayer/Listings/AVFoundationPiPPlayer_PlayerViewController_swift.html#//apple_ref/doc/uid/TP40016166-AVFoundationPiPPlayer_PlayerViewController_swift-DontLinkElementID_4

리스터 예제와 마찬가지로 https://developer.apple.com/library/ios/samplecode/Lister/Listings/Lister_ListCell_swift.html#//apple_ref/doc/uid/TP40014701-Lister_ListCell_swift-DontLinkElementID_57

핵심 위치 예와 마찬가지로 https://developer.apple.com/library/ios/samplecode/PotLoc/Listings/Potloc_PotlocViewController_swift.html#//apple_ref/doc/uid/TP40016176-Potloc_PotlocViewController_swift-DontLinkElementID_6

뷰 컨트롤러 미리보기 예와 같이 https://developer.apple.com/library/ios/samplecode/ViewControllerPreviews/Listings/Projects_PreviewUsingDelegate_PreviewUsingDelegate_DetailViewController_swift.html#//apple_ref/doc/uid/TP40016546-Projects_PreviewUsingDelegate_PreviewUsingDelegate_PreviewUsingDelegate_PreviewUsingDelegate_PreviewUsingDelegate_PreviewUsingView

HomeKit 예제와 마찬가지로 https://developer.apple.com/library/ios/samplecode/HomeKitCatalog/Listings/HMCatalog_Homes_Action_Sets_ActionSetViewController_swift.html#//apple_ref/doc/uid/TP40015048-HMCatalog_Homes_Action_Sets_ActionntViewLink_Sets_ActionntViewElement_

모두 iOS 9 용으로 완전히 업데이트되었으며 모두 약한 콘센트를 사용합니다. 이것으로부터 우리는 A를 알게됩니다. 어떤 사람들은 그것을 만드는 것처럼 간단하지 않습니다. B. 애플은 마음이 반복적으로 바뀌었고 C. 당신은 행복하게 만드는 것을 사용할 수 있습니다 :)

설명을 해준 Paul Hudson (www.hackingwithsift.com의 저자)에게 특별한 감사의 말을 전합니다.

이것이 주제를 조금 더 명확하게하기를 바랍니다.

조심해


나는이 문제를 잠시 동안 확인했지만 구체적인 대답을 찾지 못했습니다. 위의 링크는 둘 다 훌륭하고 일반적으로 Xcode가 자동 제안하는 것과 관련이 있음을 시사합니다.
subin272 2019



5

수년에 걸쳐 변화된 것처럼 보이며 이제 Apple은 일반적으로 강력하게 사용할 것을 권장합니다. WWDC 세션에 대한 증거는 세션 407- 인터페이스 빌더에서 UI 디자인 구현에 있으며 32:30에 시작합니다. 그가 말한 것에 대한 나의 메모는 (거의 정확하게 말하지는 않지만 그를 인용) :

  • 뷰 계층 구조에서 항상 유지되지 않는 하위 뷰 또는 제약 조건을 연결하는 경우 일반적으로 콘센트 연결이 강해야합니다.

  • 보기 계층 구조에서 백업 된 것에 대한 참조가있는 사용자 정의보기를 작성할 때 약한 콘센트 연결이 필요할 수 있으며 일반적으로 권장되지 않습니다.

다른 와드에서는 일부 사용자 정의보기가보기 계층에서 일부보기가있는 유지주기를 만들지 않는 한 항상 강력해야합니다.

편집하다 :

일부는 질문을 할 수 있습니다. 강력한 참조로 유지해도 루트 뷰 컨트롤러와 소유 뷰가 참조를 유지하므로 유지주기가 생성되지 않습니까? 또는 왜 그 변화가 일어 났습니까? 나는 xib에서 펜촉을 만드는 방법을 설명 할 때이 연설의 초기에 답이 있다고 생각합니다. VC와 뷰에 대해 별도의 펜촉이 생성됩니다. 나는 이것이 권장 사항을 변경하는 이유 일 수 있다고 생각합니다. 여전히 애플로부터 더 깊은 설명을 얻는 것이 좋을 것입니다.


4

가장 중요한 정보는 다음과 같습니다. xib의 요소는 자동으로 하위 관점에 있습니다. 서브 뷰는 NSArray입니다. NSArray는 요소를 소유합니다. 등등은 그들에게 강한 포인터를 가지고 있습니다. 따라서 대부분의 경우 다른 강력한 포인터 (IBOutlet)를 만들고 싶지 않습니다.

그리고 ARC를 사용하면 아무것도 할 필요가 없습니다. viewDidUnload

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.