원자 속성과 비 원자 속성의 차이점은 무엇입니까?


답변:


1761

마지막 두 개는 동일합니다. "원자"는 기본 동작입니다 ( 실제로는 키워드가 아니며nonatomicatomic , llvm / clang의 최신 버전에서는 키워드로 - 가 추가 되지 않은 경우에만 지정됨).

메소드 구현을 @synthesizing한다고 가정하면 원자 대 비 원자는 생성 된 코드를 변경합니다. 자신의 세터 / 게터를 작성하는 경우 원자 / 비 원자 / 유지 / 할당 / 복사는 단지 자문 일뿐입니다. (참고 : @synthesize는 이제 LLVM의 최신 버전에서 기본 동작입니다. 인스턴스 변수를 선언 할 필요도 없습니다. 인스턴스 변수도 자동으로 합성되며 _우발적 인 직접 액세스를 방지하기 위해 이름 앞에 추가됩니다.)

"원자"를 사용하면 합성 된 setter / getter는 다른 스레드의 setter 활동에 관계없이 getter에서 전체 값이 반환되거나 setter가 설정하도록합니다. 즉, 스레드 A가 게터의 중간에 있고 스레드 B가 세터를 호출하면 실제 실행 가능한 값 (자동 해제 된 개체)이 A의 호출자에게 반환됩니다.

에서 nonatomic그러한 보증은 이루어지지 않습니다. 따라서 nonatomic"원자"보다 상당히 빠릅니다.

"원자"가하지 않는 것은 스레드 안전을 보장하는 것입니다. 스레드 A가 스레드 B와 동시에 getter를 호출하고 C가 다른 값을 가진 setter를 호출하는 경우 스레드 A는 반환 된 세 값 중 하나 또는 설정자에 전달 된 값 중 하나를 리턴 할 수 있습니다. 마찬가지로, 객체는 알 수없는 방식으로 B 또는 C의 값으로 끝날 수 있습니다.

멀티 스레드 프로그래밍의 주요 과제 중 하나 인 데이터 무결성 보장은 다른 방법으로 달성됩니다.

이것에 추가 :

atomicity 단일 속성 중 하나는 여러 종속 속성이 재생 중일 때 스레드 안전성을 보장 할 수 없습니다.

치다:

 @property(atomic, copy) NSString *firstName;
 @property(atomic, copy) NSString *lastName;
 @property(readonly, atomic, copy) NSString *fullName;

이 경우 스레드 A는을 호출 setFirstName:한 다음 호출 하여 객체의 이름을 바꿀 수 있습니다 setLastName:. 그 동안 스레드 B는 fullName스레드 A의 두 호출 사이에서 호출 할 수 있으며 이전 성과 결합 된 새로운 이름을받습니다.

이를 해결하려면 트랜잭션 모델 이 필요합니다 . 즉 fullName, 종속 속성이 업데이트되는 동안 액세스를 제외 할 수있는 다른 종류의 동기화 및 / 또는 제외 .


21
스레드 안전 코드가 자체 잠금 등을 수행한다고 가정하면 원자 속성 접근자를 언제 사용하고 싶습니까? 좋은 모범을 생각하는 데 어려움을 겪고 있습니다.
Daniel Dickison

8
@bbum 이해합니다. 스레드 안전성이 모델 수준의 관심사라는 또 다른 답변에 대한 귀하의 의견이 마음에 듭니다. IBM 스레드 안전 정의에서 : ibm.co/yTEbjY "클래스가 올바르게 구현 된 경우, 이는 스펙을 준수 함을 나타내는 또 다른 방법입니다. 조작 시퀀스가 ​​없습니다 (공개 필드 읽기 또는 쓰기 및 공용 메소드 호출) 해당 클래스의 오브젝트에서 오브젝트를 유효하지 않은 상태로 만들거나 오브젝트가 유효하지 않은 상태 인 것을 관찰하거나 클래스의 불변, 전제 조건 또는 사후 조건을 위반할 수 있어야합니다. "
벤 플린

6
다음은 @StevenKramer와 비슷한 예 @property NSArray* astronomicalEvents;입니다. UI에 표시하려는 데이터가 나열된 예가 있습니다. 애플리케이션이 시작되면 포인터가 빈 배열을 가리키면 앱이 웹에서 데이터를 가져옵니다. 웹 요청이 (다른 스레드에서) 완료되면 앱이 새 배열을 작성하고 속성을 원자 적으로 새 포인터 값으로 설정합니다. 스레드 안전하고 빠진 것이 아니라면 잠금 코드를 작성할 필요가 없습니다. 나에게 꽤 유용한 것 같습니다.
bugloaf

10
@HotLicks 또 다른 재미있는 것; 특정 아키텍처에서 (어떤 것을 기억할 수 없는지), 인수로 전달 된 64 비트 값은 레지스터에서 절반으로, 스택에서 절반으로 전달 될 수 있습니다. atomic교차 스레드 반값 읽기를 방지합니다. (추적하기에 재미있는 버그였습니다.)
bbum

8
@congliu 스레드 A는 retain/autorelease춤을 추지 않고 객체를 반환합니다 . 스레드 B가 객체를 해제합니다. 스레드 A가 붐을 일으 킵니다 . atomic스레드 A가 리턴 값에 대한 강력한 참조 (+1 보유 횟수)를 갖도록합니다.
bbum

360

이것은 Apple의 문서에 설명되어 있지만 아래는 실제로 일어나는 일의 예입니다.

"atomic"키워드가 없으며 "nonatomic"을 지정하지 않으면 속성이 원자 적이지만 "atomic"을 명시 적으로 지정하면 오류가 발생합니다.

"비 원자"를 지정하지 않으면 속성이 원자 적이지만 원하는 경우 최신 버전에서 "원자"를 명시 적으로 지정할 수 있습니다.

//@property(nonatomic, retain) UITextField *userName;
//Generates roughly

- (UITextField *) userName {
    return userName;
}

- (void) setUserName:(UITextField *)userName_ {
    [userName_ retain];
    [userName release];
    userName = userName_;
}

이제 원자 변형은 조금 더 복잡합니다.

//@property(retain) UITextField *userName;
//Generates roughly

- (UITextField *) userName {
    UITextField *retval = nil;
    @synchronized(self) {
        retval = [[userName retain] autorelease];
    }
    return retval;
}

- (void) setUserName:(UITextField *)userName_ {
    @synchronized(self) {
      [userName_ retain];
      [userName release];
      userName = userName_;
    }
}

기본적으로 원자 버전은 스레드 안전성을 보장하기 위해 잠금을 수행해야하며 호출자에게 객체가 존재하도록 객체의 참조 횟수와 객체의 균형을 잡기 위해 자동 해제 횟수를 충돌시킵니다. 다른 스레드가 값을 설정하여 참조 횟수가 0으로 떨어지면 잠재적 경쟁 조건입니다.

실제로 속성이 스칼라 값인지 개체인지, 유지, 복사, 읽기 전용, 비 원자 등이 상호 작용하는 방식에 따라 이러한 방식이 작동하는 방식에는 여러 가지 변형이 있습니다. 일반적으로 속성 신디사이저는 모든 조합에 대해 "올바른 일"을 수행하는 방법 만 알고 있습니다.


8
@Louis Gerbarg : 동일한 객체 (즉, userName == userName_)를 할당하려고하면 (비 원자, 리테이너) 세터 버전이 제대로 작동하지 않는다고 생각합니다.
Florin

5
코드가 약간 잘못되었습니다. 어떤 원자 게터 / 세터가 동기화되는지에 대한 보장 은 없습니다 . 비판적으로, @property (assign) id delegate;아무것도 동기화되지 않습니다 (iOS SDK GCC 4.2 ARM -Os) . [self.delegate delegateMethod:self];와 사이에 경쟁이 있음을 의미 foo.delegate = nil; self.foo = nil; [super dealloc];합니다. stackoverflow.com/questions/917884/…
tc를

@fyolnish 나는 _val/ val가 무엇인지 확실 하지 않지만 실제로는 아닙니다. 원자 copy/ retain속성에 대한 getter 는 다른 스레드에서 setter가 호출되어 참조 횟수가 0이되는 객체를 반환하지 않도록해야합니다. 본질적으로 ivar를 읽고 유지하면서 setter 가하지 않도록해야합니다. 덮어 쓰고 해제 한 다음 자동 해제하여 보존 상태를 조정합니다. 그것은 본질적으로 getter와 setter가 잠금을 사용해야 함을 의미 합니다 (메모리 레이아웃이 수정 된 경우 CAS2 명령으로 가능해야합니다. 아아 -retain는 메서드 호출입니다).
tc.

@ tc 그것은 꽤 오래되었지만 내가 쓸 생각은 아마 다음과 같습니다 : gist.github.com/fjolnir/5d96b3272c6255f6baae 그러나 그렇습니다. 리더가 그것을 반환합니다. 그러나 setter가 -release 대신 -autorelease를 사용하면 문제가 해결됩니다.
Fjölnir

@fyolnish 불행히도, 아니오 : setter의 스레드에서 자동 릴리스되고 getter의 스레드에서 자동 릴리스되어야합니다. 또한 재귀를 사용하기 때문에 스택이 부족할 가능성이있는 것처럼 보입니다.
tc.

170

원자

  • 기본 동작입니다
  • 다른 프로세스가 변수에 액세스하기 전에 현재 프로세스가 CPU에 의해 완료되도록합니다.
  • 프로세스가 완전히 완료되도록 보장하기 때문에 빠르지 않습니다.

비 원자

  • 기본 동작이 아닙니다
  • 더 빠름 (합성 코드, 즉 @property 및 @synthesize를 사용하여 작성된 변수의 경우)
  • 스레드 안전하지 않은
  • 서로 다른 두 프로세스가 동일한 변수에 동시에 액세스 할 때 예기치 않은 동작이 발생할 수 있습니다.

137

차이점을 이해하는 가장 좋은 방법은 다음 예제를 사용하는 것입니다.

"name"이라는 원자 문자열 특성이 있다고 가정 [self setName:@"A"]하고 스레드 A에서 호출 [self setName:@"B"]하고 스레드 B에서 호출 [self name]하고 스레드 C에서 호출 하면 다른 스레드에 대한 모든 조작이 순차적으로 수행되며 이는 하나의 스레드가 setter를 실행 중임을 의미합니다. 또는 getter이면 다른 스레드가 기다립니다.

이렇게하면 속성 "name"이 (가) 읽기 / 쓰기 안전 상태가되지만 다른 스레드 D가 [name release]동시에 호출 하면 여기에 관련된 setter / getter 호출이 없기 때문에이 작업으로 인해 충돌이 발생할 수 있습니다. 즉, 다른 스레드가 동시에 모든 유형의 메시지를 객체에 보낼 수 있으므로 객체는 읽기 / 쓰기 안전 (ATOMIC)이지만 스레드로부터 안전하지 않습니다. 개발자는 이러한 객체에 대한 스레드 안전성을 보장해야합니다.

"name"속성이 원자가 아닌 경우 위의 예-A, B, C 및 D에있는 모든 스레드가 동시에 실행되어 예기치 않은 결과가 생성됩니다. 원자의 경우 A, B 또는 C 중 하나가 먼저 실행되지만 D는 여전히 병렬로 실행될 수 있습니다.


116

구문과 의미는 이미이 질문에 대한 다른 훌륭한 답변으로 잘 정의되어 있습니다. 때문에 실행성능을 잘 설명되지 않습니다, 나는 내 대답을 추가합니다.

이 3의 기능적 차이점은 무엇입니까?

나는 항상 원자를 기본적으로 매우 흥미로운 것으로 생각했습니다. 추상화 수준에서 클래스의 원자 속성을 차량으로 사용하여 100 % 스레드 안전성을 달성하는 것이 가장 중요합니다. 진정한 올바른 멀티 스레드 프로그램을 위해서는 프로그래머의 개입이 거의 필요합니다. 한편, 성능 특성 및 실행은 아직 자세히 설명되지 않았습니다. 수년 동안 엄청나게 멀티 스레드 된 프로그램을 작성해온 결과, nonatomic원자는 어떤 목적으로도 감지 할 수 없었기 때문에 내 속성을 전체적으로 선언했습니다 . 이 질문에 대한 원자 및 비 원자 특성의 세부 사항을 논의하는 동안 일부 프로파일 링에서 흥미로운 결과가 발생했습니다.

실행

확인. 가장 먼저 정리하고 싶은 것은 잠금 구현이 구현 정의되고 추상화된다는 것입니다. Louis는 @synchronized(self)그의 예에서 사용합니다 . 나는 이것을 일반적인 혼란의 근원으로 보았습니다. 구현은 실제로 사용 하지 않습니다 @synchronized(self). 객체 레벨 스핀 잠금을 사용합니다 . Louis의 일러스트레이션은 우리 모두에게 친숙한 구문을 사용하는 고급 일러스트레이션에는 적합하지만 사용하지 않는 것이 중요합니다 @synchronized(self).

또 다른 차이점은 원자 속성이 게터 내에서 객체를 유지 / 해제하는 것입니다.

공연

흥미로운 부분은 다음과 같습니다. 경합되지 않은 (예 : 단일 스레드) 경우 에 원자 속성 액세스를 사용하는 성능 은 경우에 따라 매우 빠릅니다. 이상적인 경우보다 적은 경우 원자 액세스를 사용하면 오버 헤드의 20 배 이상이 소요될 수 있습니다 nonatomic. 7 개의 스레드를 사용 하는 컨테스트 된 케이스는 3 바이트 구조 (2.2GHz 코어 i7 쿼드 코어, x86_64)의 경우 44 배 느 렸습니다 . 3 바이트 구조체는 매우 느린 속성의 예입니다.

흥미로운 참고 사항 : 3 바이트 구조체의 사용자 정의 접근자는 합성 원자 접근 자보다 52 배 빠릅니다. 또는 합성 된 비 원자 접근 자의 속도 84 %

분쟁이 발생한 경우의 개체도 50 배를 초과 할 수 있습니다.

구현의 최적화 및 변형이 많기 때문에 이러한 상황에서 실제 영향을 측정하는 것은 매우 어렵습니다. "프로필을 작성하여 문제가 발견되지 않는 한 신뢰하기"와 같은 소리가 들릴 수 있습니다. 추상화 수준으로 인해 실제로 실제 영향을 측정하는 것은 매우 어렵습니다. 프로파일에서 실제 비용을 모으는 데는 시간이 많이 걸리고 추상화로 인해 매우 정확하지 않을 수 있습니다. 또한 ARC 대 MRC는 큰 차이를 만들 수 있습니다.

따라서 속성 액세스 구현에 중점을 두지 않고 뒤로 물러서 봅시다 . 우리는 일반적인 용의자를 포함시키고 objc_msgSend, 경합되지 않은 경우 NSString게터에 대한 많은 호출에 대한 실제 높은 수준의 결과를 조사합니다 (초 단위 값).

  • MRC | 비 원자 | 수동으로 구현 된 게터 : 2
  • MRC | 비 원자 | 합성 게터 : 7
  • MRC | 원자 | 합성 게터 : 47
  • 아크 | 비 원자 | 합성 게터 : 38 (참고 : ARC의 ref 카운트 사이클링 추가)
  • 아크 | 원자 | 합성 게터 : 47

짐작할 수 있듯이, 참조 카운트 활동 / 사이클링은 원자와 ARC 하에서 중요한 기여자입니다. 또한 이의 제기 된 사례에서 더 큰 차이를 볼 수 있습니다.

나는 성능에 세심한주의를 기울이지 만, 여전히 Semantics First 라고 말합니다 ! . 한편, 많은 프로젝트에서 성능은 우선 순위가 낮습니다. 그러나 실행 세부 사항과 사용하는 기술 비용을 아는 것이 확실합니다. 필요, 목적 및 능력에 맞는 기술을 사용해야합니다. 이 방법을 사용하면 몇 시간의 비교 시간을 절약하고 프로그램을 디자인 할 때보다 정확한 결정을 내릴 수 있기를 바랍니다.


MRC | 원자 | 합성 게터 : 47 아크 | 원자 | 합성 게터 : 47 그것들을 동일하게 만드는 것은 무엇입니까? ARC에 더 많은 오버 헤드가 없어야합니까?
SDEZero

2
따라서 원자 속성이 나쁜 경우 기본값입니다. 상용구 코드를 늘리려면?
Kunal Balani

@ LearnCocos2D 방금 동일 NSString하지 않은 단일 스레드 케이스에 대해 10.8을 목표로 동일한 컴퓨터에서 10.8.5로 테스트했습니다 -ARC atomic (BASELINE): 100% -ARC nonatomic, synthesised: 94% -ARC nonatomic, user defined: 86% -MRC nonatomic, user defined: 5% -MRC nonatomic, synthesised: 19% -MRC atomic: 102%. 결과는 오늘날과 조금 다릅니다. 나는 @synchronized비교를 하지 않았다 . @synchronized의미 론적으로 다르며, 사소한 동시 프로그램이 있으면 좋은 도구라고 생각하지 않습니다. 속도가 필요한 경우 피하십시오 @synchronized.
저스틴

이 테스트를 온라인 어딘가에 있습니까? 여기에 계속 추가합니다 : github.com/LearnCocos2D/LearnCocos2D/tree/master/…
LearnCocos2D

@ LearnCocos2D 나는 인간 소비를 위해 그들을 준비하지 않았습니다. 죄송합니다.
저스틴

95

원자 = 스레드 안전

비 원자 = 나사산 안전 없음

스레드 안전 :

인스턴스 변수는 런타임 환경에 의한 스레드 실행의 스케줄링 또는 인터리빙에 관계없이 호출 스레드에서 추가 동기화 또는 기타 조정없이 여러 스레드에서 액세스 할 때 올바르게 작동하는 경우 스레드 안전합니다.

우리의 맥락에서 :

스레드가 인스턴스 값을 변경하면 변경된 값을 모든 스레드에서 사용할 수 있으며 한 번에 하나의 스레드 만 값을 변경할 수 있습니다.

사용처 atomic:

다중 스레드 환경에서 인스턴스 변수에 액세스하는 경우

의 의미 atomic:

런타임에서 워치 독 작업이 필요하지 않기 nonatomic때문에 빠르지 nonatomic않습니다.

사용처 nonatomic:

여러 스레드에서 인스턴스 변수를 변경하지 않을 경우 사용할 수 있습니다. 성능이 향상됩니다.


3
당신이 여기서 말하는 모든 것은 맞지만, 마지막 문장은 오늘날의 프로그래밍에있어 본질적으로 "잘못된"Dura입니다. 이런 식으로 "성능을 향상 시키려고"노력하는 것은 정말로 상상할 수없는 일입니다. (당신이 광년 안에 들어가기 전에, 당신은 "ARC를 사용하지 않고", "NSString을 사용하지 않기 때문에 사용하지 않는 것"등을 의미합니다.) 극단적 인 예를 들자면, "팀, 속도가 느려지므로 코드에 주석을 달지 마십시오. " 신뢰할 수없는 이유로 (존재하지 않는) 이론적 성능 향상을 원하는 현실적인 개발 파이프 라인은 없습니다.
Fattie

3
@JoeBlow 사실은 여기서 확인할 수 있습니다. developer.apple.com/library/mac/documentation/Cocoa/Conceptual/…
Durai Amuthan.H

1
FWIW의 Durai는이 링크 가“원자 = 스레드 안전성”이라는 논문과 직접 ​​모순됩니다. 이 문서에서 Apple은 명시 적으로“재산 원자 성은 객체의 스레드 안전성과 동의어가 아닙니다.”라고 말합니다. 실제로 원자는 스레드 안전성을 달성하기에 충분하지 않습니다.
Rob

69

원자 속성과 비 원자 속성에 대한 설명을 여기에서 찾아 냈습니다 . 다음은 동일한 관련 텍스트입니다.

'원자'는 분류 할 수 없음을 의미합니다. OS / 프로그래밍 용어에서 원자 함수 호출은 중단 할 수없는 호출입니다. 전체 함수를 실행해야하며, 완료 될 때까지 OS의 일반적인 컨텍스트 전환으로 CPU에서 스왑하지 않아야합니다. 알지 못하는 경우를 대비하여 : CPU는 한 번에 하나의 작업 만 수행 할 수 있으므로 OS는 작은 시간 단위로 실행중인 모든 프로세스에 대한 CPU 액세스를 환상으로 전환합니다.멀티 태스킹 CPU 스케줄러는 실행 중 언제라도 (중간 함수 호출에서도) 프로세스를 중단시킬 수 있습니다. 따라서 두 프로세스가 동시에 변수를 업데이트하려고 할 수있는 공유 카운터 변수 업데이트와 같은 작업의 경우 '원자 적으로'실행되어야합니다. 즉, 각 프로세스가 다른 프로세스로 교체되기 전에 각 업데이트 작업이 완전히 완료되어야합니다. CPU.

따라서이 경우 원자는 속성 판독기 메소드를 중단 할 수 없다는 것을 의미합니다. 실제로 메소드가 읽은 변수가 다른 스레드 / 호출 / 함수가 가져 오기 때문에 절반의 값을 변경할 수 없음을 의미합니다. CPU로 교체되었습니다.

atomic변수를 인터럽트 할 수 없으므로 어느 시점에서든 변수에 포함 된 값은 (스레드 잠금) 손상되지 않도록 보장되지만이 스레드 잠금을 보장 하면 변수에 대한 액세스 속도가 느려집니다. non-atomic반면에 변수는 그러한 보장을하지 않지만 더 빠른 액세스를 제공합니다. 요약하면 non-atomic여러 스레드가 동시에 변수에 액세스하지 않고 속도를 높일 수 있다는 것을 알고있을 때 계속 하십시오.


1
링크가 끊어졌습니다. ; (
Rob

그것은 링크의 문제입니다 :(
행운스럽게

67

너무 많은 기사를 읽고 Stack Overflow 게시물을 작성하고 데모 응용 프로그램에서 가변 속성 속성을 확인한 후 모든 속성 정보를 하나로 묶기로 결정했습니다.

  1. atomic // 기본
  2. nonatomic
  3. strong = retain // 기본
  4. weak = unsafe_unretained
  5. retain
  6. assign // 기본
  7. unsafe_unretained
  8. copy
  9. readonly
  10. readwrite // 기본

iOS의 변수 속성 속성 또는 수정 자 기사 에서 위에서 언급 한 모든 속성을 찾을 수 있으며 이는 확실히 도움이 될 것입니다.

  1. atomic

    • atomic 하나의 스레드 만 변수에 액세스 함을 의미합니다 (정적 유형).
    • atomic 스레드 안전합니다.
    • 그러나 성능이 느립니다.
    • atomic 기본 동작입니다
    • 가비지 수집되지 않은 환경 (예 : 유지 / 릴리스 / 자동 릴리스 사용)의 원자 접근자는 다른 스레드가 값의 올바른 설정 / 가져 오기를 방해하지 않도록 잠금을 사용합니다.
    • 실제로는 키워드가 아닙니다.

    예:

        @property (retain) NSString *name;
    
        @synthesize name;
  2. nonatomic

    • nonatomic 변수에 대한 다중 스레드 액세스 (동적 유형)를 의미합니다.
    • nonatomic 스레드 안전하지 않습니다.
    • 그러나 성능이 빠릅니다.
    • nonatomic기본 동작이 아닙니다. nonatomic속성 속성에 키워드 를 추가해야 합니다.
    • 두 개의 서로 다른 프로세스 (스레드)가 동시에 동일한 변수에 액세스 할 때 예기치 않은 동작이 발생할 수 있습니다.

    예:

        @property (nonatomic, retain) NSString *name;
    
        @synthesize name;

할당 및 강점 / 유지 둘 다 기본값으로 설정하는 방법은 무엇입니까?
BangOperator

ARC와 함께 제공됩니다. ARC 이전의 기본값 유지
abdullahselek

56

원자력 :

원자는 속성에 대한 액세스가 원자 방식으로 수행되도록 보장합니다. 예를 들어 항상 완전히 초기화 된 객체를 반환합니다. 한 스레드의 속성 가져 오기 / 설정은 다른 스레드가 액세스하기 전에 완료해야합니다.

두 개의 스레드에서 한 번에 다음 기능이 발생한다고 생각하면 결과가 좋지 않은 이유를 알 수 있습니다.

-(void) setName:(NSString*)string
{
  if (name)
  {
    [name release]; 
    // what happens if the second thread jumps in now !?
    // name may be deleted, but our 'name' variable is still set!
    name = nil;
  }

  ...
}

장점 : 매번 완전히 초기화 된 객체를 반환하면 멀티 스레딩의 경우 최상의 선택입니다.

단점 : 성능 저하, 실행 속도가 약간 느려짐

비 원자 :

원자와 달리 매번 완전히 초기화 된 객체 반환을 보장하지는 않습니다.

장점 : 매우 빠른 실행.

단점 : 멀티 스레딩의 경우 가비지 값이 발생할 가능성이 있습니다.


5
그 의견은 이해가되지 않습니다. 당신은 명확히 할 수 있습니까? Apple 사이트에서 예제를 보면 atomic 키워드가 해당 특성을 업데이트하는 동안 오브젝트에서 동기화됩니다.
앤드류 그랜트

52

가장 쉬운 답변 : 두 번째 예제 사이에는 차이가 없습니다. 기본적으로 속성 접근자는 원자 적입니다.

가비지 수집되지 않은 환경 (예 : 유지 / 릴리스 / 자동 릴리스 사용)의 원자 접근자는 다른 스레드가 값의 올바른 설정 / 가져 오기를 방해하지 않도록 잠금을 사용합니다.

멀티 스레드 응용 프로그램을 만들 때의 추가 정보 및 기타 고려 사항에 대해서는 Apple Objective-C 2.0 설명서 의 " 성능 및 스레딩 "섹션을 참조하십시오.


8
두 가지 이유가 있습니다. 먼저 합성 코드의 경우 더 빠르지 만 스레드 안전 코드는 생성하지 않습니다. 둘째, 원자가 아닌 고객 접근자를 작성하는 경우 코드를 구현하지 않고도 인터페이스를 읽을 때 코드가 원자가 아님을 향후 사용자에게 주석을 달 수 있습니다.
Louis Gerbarg


31

원자는 하나의 스레드 만 변수 (정적 유형)에 액세스 함을 의미합니다. 원자는 스레드로부터 안전하지만 느립니다.

비원 자란 다중 스레드가 변수 (동적 유형)에 액세스 함을 의미합니다. 비원자는 스레드 안전하지 않지만 빠릅니다.


14

원자는 스레드 안전 , 그것은 천천히 그리고 잘 보증한다 (보장 할 수 없습니다) 많은 스레드가 동일한 영역에 대한 액세스를 시도하는 방법에만 고정 값에 상관없이 제공되지 않습니다. 아토믹 (atomic)을 사용할 때,이 함수 안에 작성된 코드 조각은 한 번에 하나의 스레드 만 실행할 수있는 중요 섹션의 일부가됩니다.

스레드 안전 만 보장합니다. 보장하지 않습니다. 내 말은 당신이 당신의 차를 위해 전문 운전자를 고용한다는 것입니다. 그럼에도 불구하고 자동차가 사고를 당하지 않을 것이라고 보장하지는 않습니다. 그러나 가능성은 가장 적습니다.

원자-그것은 나눌 수 없으므로 결과가 기대됩니다. 비 원자-다른 스레드가 메모리 영역에 액세스하면이를 수정할 수 있으므로 예상치 못한 결과가 발생합니다.

코드 토크 :

원자는 속성 스레드의 getter 및 setter를 안전하게 만듭니다. 예를 들어 u가 다음과 같이 쓴 경우 :

self.myProperty = value;

스레드 안전합니다.

[myArray addObject:@"Abc"] 

스레드 안전하지 않습니다.


마지막 단락이 어떻게 나오는지 모르겠지만 간단하지 않습니다. "비공개 사본"과 같은 것은 없습니다.
피크

13

"atomic"이라는 키워드가 없습니다

@property(atomic, retain) UITextField *userName;

위와 같이 사용할 수 있습니다

@property(retain) UITextField *userName;

@property (atomic, retain) NSString * myString을 사용하면 문제가 발생하는 스택 오버플로 질문을 참조하십시오 .


10
"이러한 키워드가 있습니다."라는 키워드는 기본적으로 필요하지 않으며 심지어 기본값 일지라도 키워드가 존재하지 않는다는 의미는 아닙니다.
Matthijn

4
이것은 올바르지 않습니다. 키워드가 존재합니다. 이 답변은 오해의 소지가 있으므로이 답변을 삭제하는 것이 좋습니다.
sethfri

12

원자 (기본값)

원자가 기본값입니다. 아무 것도 입력하지 않으면 속성이 원 자성입니다. 원자 속성은 그 속성을 읽으려고하면 유효한 값을 얻습니다. 그 값이 무엇인지에 대해 보증하지는 않지만 정크 메모리뿐만 아니라 좋은 데이터를 얻을 수 있습니다. 이것이 할 수있는 것은 단일 변수를 가리키는 다중 스레드 또는 다중 프로세스가있는 경우 하나의 스레드가 읽고 다른 스레드가 쓸 수있는 것입니다. 동시에 충돌하면 리더 스레드는 변경 전 또는 변경 후 두 값 중 하나를 얻습니다. 원자가 제공하지 않는 것은 어떤 값을 얻을 수 있는지에 대한 보증입니다. 원자는 실제로 스레드 안전과 혼동되기 때문에 정확하지 않습니다. 다른 방법으로 스레드 안전을 보장해야합니다.

비 원자

반대로, 원자가 아닌 것은 아마도 "원자 적 일을하지 말라"는 것을 의미 할 것입니다. 당신이 잃어버린 것은 항상 무언가를 되 찾는다는 보장입니다. 쓰기 도중에 읽으려고하면 가비지 데이터를 다시 얻을 수 있습니다. 그러나 다른 한편으로는 조금 더 빠릅니다. 원자 속성은 값을 되 찾을 수 있도록 약간의 마법을 수행해야하기 때문에 조금 느립니다. 그것이 당신이 많이 접근하는 재산이라면, 당신은 그 속도 패널티가 발생하지 않도록 비 원자력으로 드롭 다운 할 수 있습니다.

자세한 내용은 여기를 참조하십시오 : https://realm.io/news/tmi-objective-c-property-attributes/


11

기본 이다 atomic이 방법은 당신이 속성을 사용할 때마다 당신에게 성능 비용 않지만, 스레드 안전합니다. Objective-C는 잠금을 설정하므로 setter / getter가 실행되는 한 실제 스레드 만 변수에 액세스 할 수 있습니다.

ivar _internal이있는 속성의 MRC를 사용한 예 :

[_internal lock]; //lock
id result = [[value retain] autorelease];
[_internal unlock];
return result;

마지막 두 개는 동일합니다.

@property(atomic, retain) UITextField *userName;

@property(retain) UITextField *userName; // defaults to atomic

반면에 nonatomic코드 에는 아무것도 추가하지 않습니다. 따라서 보안 메커니즘을 직접 코딩하는 경우에만 스레드 안전합니다.

@property(nonatomic, retain) UITextField *userName;

키워드를 첫 번째 속성 속성으로 쓰지 않아도됩니다.

잊지 마십시오. 이것이 속성 전체가 스레드로부터 안전하다는 것을 의미하지는 않습니다. setter / getter의 메소드 호출 만 있습니다. 그러나 setter를 사용하고 그 후에 getter를 동시에 2 개의 다른 스레드로 사용하면 깨질 수도 있습니다!


10

시작하기 전에 : 새 라이터가 발생하려면 메모리의 모든 객체를 메모리에서 할당 해제해야합니다. 종이에 쓰는 것처럼 단순히 위에 글을 쓸 수는 없습니다. 당신은 있어야합니다 첫 번째 (할당 해제)을 삭제를하고 당신은 그것에 쓸 수 있습니다. 지우기가 완료된 (또는 절반이 완료된) 시점에서 아직 쓰지 않은 (또는 절반이 쓴) 아무것도 읽지 않으면 매우 문제가 될 수 있습니다! 원자 및 비 원자 (nonatomic)는이 문제를 다른 방식으로 처리하는 데 도움이됩니다.

먼저 읽기 질문을하고 읽을 Bbum의 답변을 . 또한 내 요약을 읽으십시오.


atomic 항상 보증합니다

  • 서로 다른 두 사람이 동시에 읽고 쓰기를 원한다면 종이가 타지 않을 것입니다! -> 경쟁 조건에서도 애플리케이션이 중단되지 않습니다.
  • 한 사람이 글을 쓰려고하고 8 글자 중 4 글자 만 쓰면 중간에 읽을 수 없으며, 8 글자를 모두 쓸 때만 읽을 수 있습니다-> 읽지 않음 '쓰고있는 스레드', 즉 8 바이트에서 바이트로 쓸 바이트가 있고 4 바이트 만 쓰면 그 시점까지 읽을 수 없습니다. 나는 그 다음 충돌하지 것이라고 말했다하지만 이후 그것은의 값에서 읽을 것 오토 릴리즈 객체입니다.
  • 만약 전에 당신이 쓰는 이전에 종이에 작성하고 누군가가 읽고 싶어 한 것을 삭제 할 수 있습니다 아직 읽어 보시기 바랍니다. 어떻게? Mac OS 휴지통과 비슷한 것을 읽을 것입니다 (휴지통은 여전히 ​​100 % 지워지지 않기 때문에 ... 림보에 있습니다) ---> ThreadB가 이미 쓰기 할당을 해제 한 상태에서 ThreadA를 읽으면 얻을 수 있습니다. ThreadB에 의해 완전히 작성된 최종 값 또는 자동 릴리스 풀에서 값을 가져 오는 값

보유 횟수는 Objective-C에서 메모리가 관리되는 방식입니다. 객체를 만들 때 유지 횟수는 1입니다. 객체에 보관 메시지를 보내면 보관 횟수가 1 씩 증가합니다. 객체를 릴리즈 메시지를 보내면 보관 횟수가 1 씩 감소합니다. 객체에 자동 해제 메시지를 보내면 향후 보유 단계에서 보유 횟수가 1 씩 감소합니다. 오브젝트의 보유 횟수가 0으로 줄어들면 할당이 해제됩니다.

  • 원자 안전은 스레드 안전을 보장 하지는 않지만 스레드 안전을 달성하는 데 유용합니다. 스레드 안전성은 코드 작성 방법 / 읽고 쓰는 스레드 대기열과 관련이 있습니다. 충돌 불가능한 멀티 스레딩 만 보장합니다.

뭐?! 멀티 스레딩과 스레드 안전성이 다른가요?

예. 멀티 스레딩은 여러 스레드가 동시에 공유 된 데이터 조각을 읽을 수 있으며 충돌하지는 않지만 자동 릴리스되지 않은 값에서 읽지 않는다고 보장하지는 않습니다. 스레드 안전을 사용하면 읽은 내용이 자동으로 해제되지 않습니다. 우리가 기본적으로 모든 것을 원자 적으로 만들지 않는 이유는 성능 비용이 있고 대부분의 경우 스레드 안전이 필요하지 않기 때문입니다. 코드의 일부에는 필요하고 그 일부에는 잠금, 뮤텍스 또는 동기화를 사용하여 스레드 안전 방식으로 코드를 작성해야합니다.


nonatomic

  • Mac OS 휴지통과 같은 것은 없기 때문에 아무도 당신이 항상 가치를 얻는 지 (<-잠재적으로 충돌을 일으킬 수 있음) 상관하지 않으며 누군가가 당신의 글을 반쯤 읽으려고 할 때 아무도 신경 쓰지 않습니다. 메모리에 반만 쓰는 것은 종이에 반쯤 쓰는 것과는 매우 다릅니다. 메모리에 그것은 이전부터 미친 바보 같은 가치를 줄 수 있지만, 종이에는 쓰여진 것의 절반 만 볼 수 있습니다) 자동 릴리스 메커니즘을 사용하지 않습니다.
  • 전체 기록 값을 읽도록 보장하지는 않습니다!
  • 원자보다 빠름

전반적으로 두 가지 측면에서 다릅니다.

  • 자동 릴리스 풀이 있거나 없어서 충돌 여부

  • '아직 완료되지 않은 쓰기 또는 빈 값'중간에 바로 읽도록 허용하거나 값이 완전히 쓰여질 때만 읽도록 허용합니다 .


9

다중 스레드 코드에서 속성을 사용하는 경우 비 원자 속성과 원자 속성의 차이를 볼 수 있습니다. 비원자는 원자보다 빠르며 원자는 스레드가 아닌 비 원자입니다.

Vijayendra Tripathi는 이미 멀티 스레드 환경에 대한 예를 제시했습니다.


9
  • -원자는 하나의 스레드 만 변수 (정적 유형)에 액세스 함을 의미합니다.
  • 원자는 스레드 안전합니다.
  • 그러나 성능이 느립니다.

선언하는 방법 :

원자가 기본값이므로

@property (retain) NSString *name;

AND 구현 파일

self.name = @"sourov";

세 가지 속성과 관련된 작업이

 @property (retain) NSString *name;
 @property (retain) NSString *A;
 @property (retain) NSString *B;
 self.name = @"sourov";

모든 속성은 병렬 적으로 (비동기 적으로) 작동합니다.

당신이 스레드에서 "이름"을 호출하는 경우 ,

당신이 전화하면 동시에

[self setName:@"Datta"]

스레드 B 에서

* name 속성이 비 원자 라면

  • A에 대해 "Datta"값을 반환합니다.
  • B에 대해 "Datta"값을 반환합니다.

그것이 원자가 아닌 것을 스레드 안전하지 않은 이유이지만 병렬 실행으로 인해 성능이 빠릅니다.

* name 속성이 원자 인 경우

  • 그것은 "Sourov"의 가치를 보장합니다
  • 그런 다음 B에 대해 "Datta"값을 반환합니다.

스레드 안전이라는 이유 원자의 것으로 하고 그의는 그것은 읽기 - 쓰기 안전이라고하는 이유

이러한 상황 작업은 순차적으로 수행됩니다. 그리고 성능 저하

-비 원자 (Nonatomic)는 다중 스레드가 변수 (동적 유형)에 액세스하는 것을 의미합니다.

-비 원자 스레드 안전하지 않습니다.

-그러나 성능이 빠릅니다.

-Nonatomic은 기본 동작이 아니며 속성 속성에 비 원자 키워드를 추가해야합니다.

스위프트의 경우 스위프트 속성이 ObjC 의미에서 비원 자임을 확인합니다. 한 가지 이유는 속성 별 원자가 귀하의 요구에 충분한 지 여부를 생각하기 때문입니다.

참조 : https://forums.developer.apple.com/thread/25642

자세한 정보는 웹 사이트 http://rdcworld-iphone.blogspot.in/2012/12/variable-property-attributes-or.html을 방문하십시오.


4
많은 많은 많은 maaaaany 다른 사람이 말했듯이, atomic있다 하지 스레드 안전! 스레드 문제에 더 강하지 만 스레드 안전하지는 않습니다. 단지 "올바른"값 (바이너리 레벨)이라는 전체 가치를 얻을 수 있지만 비즈니스 논리에 대한 현재 및 "올바른"값임을 보장 할 수는 없습니다. 논리에 의해 유효하지 않음).
Alejandro Iván

6

원 자성 원자 (디폴트 값)

원자가 기본값입니다. 아무 것도 입력하지 않으면 속성이 원 자성입니다. 원자 속성은 그 속성을 읽으려고하면 유효한 값을 얻습니다. 그 값이 무엇인지에 대해 보증하지는 않지만 정크 메모리뿐만 아니라 좋은 데이터를 얻을 수 있습니다. 이것이 할 수있는 것은 단일 변수를 가리키는 다중 스레드 또는 다중 프로세스가있는 경우 하나의 스레드가 읽고 다른 스레드가 쓸 수있는 것입니다. 동시에 충돌하면 리더 스레드는 변경 전 또는 변경 후 두 값 중 하나를 얻습니다. 원자가 제공하지 않는 것은 어떤 값을 얻을 수 있는지에 대한 보증입니다. 원자는 실제로 스레드 안전과 혼동되기 때문에 정확하지 않습니다. 다른 방법으로 스레드 안전을 보장해야합니다.

비 원자

반대로, 원자가 아닌 것은 아마도 "원자 적 일을하지 말라"는 것을 의미 할 것입니다. 당신이 잃어버린 것은 항상 무언가를 되 찾는다는 보장입니다. 쓰기 도중에 읽으려고하면 가비지 데이터를 다시 얻을 수 있습니다. 그러나 다른 한편으로는 조금 더 빠릅니다. 원자 속성은 값을 되 찾을 수 있도록 약간의 마법을 수행해야하기 때문에 조금 느립니다. 그것이 당신이 많이 접근하는 재산이라면, 당신은 그 속도 패널티가 발생하지 않도록 비 원자력으로 드롭 다운 할 수 있습니다. 접속하다

예의 https://academy.realm.io/posts/tmi-objective-c-property-attributes/

원자 속성과 비 원자 속성은 해당 Swift 속성 선언에 반영되지 않지만 가져온 속성이 Swift에서 액세스 될 때 Objective-C 구현의 원 자성 보장은 여전히 ​​유지됩니다.

따라서 Objective-C에서 원자 속성을 정의하면 Swift에서 사용할 때 원자 속성으로 유지됩니다.

예의 https://medium.com/@YogevSitton/atomic-vs-non-atomic-properties-crash-course-d11c23f4366c


5

atomic 속성은 getter & setter를 수행하는 스레드 수에 관계없이 완전히 초기화 된 값을 유지합니다.

nonatomic 속성은 합성 된 접근자가 단순히 값을 직접 설정하거나 반환하도록 지정합니다. 동일한 값이 다른 스레드에서 동시에 액세스되는 경우에 대한 보장은 없습니다.


3

원자는 한 번에 하나의 스레드 만 변수에 액세스 할 수 있음을 의미합니다 (정적 유형). 원자는 스레드로부터 안전하지만 느립니다.

비원 자란 여러 스레드가 동시에 변수에 액세스 할 수 있음을 의미합니다 (동적 유형). 비원자는 스레드 안전하지 않지만 빠릅니다.


1

원자를 사용하는 경우 스레드가 안전하고 읽기 전용임을 의미합니다. 비원자를 사용하는 경우 여러 스레드가 변수에 액세스하고 스레드가 안전하지 않다는 것을 의미하지만 빠르게 실행되고 읽기 및 쓰기 작업이 수행됩니다. 이것은 동적 유형입니다.


1

진실은 그들이 스핀 속성을 사용하여 원자 속성을 구현한다는 것입니다. 아래와 같은 코드 :

 static inline void reallySetProperty(id self, SEL _cmd, id newValue, 
      ptrdiff_t offset, bool atomic, bool copy, bool mutableCopy) 
    {
        id oldValue;
        id *slot = (id*) ((char*)self + offset);

        if (copy) {
            newValue = [newValue copyWithZone:NULL];
        } else if (mutableCopy) {
            newValue = [newValue mutableCopyWithZone:NULL];
        } else {
            if (*slot == newValue) return;
            newValue = objc_retain(newValue);
        }

        if (!atomic) {
            oldValue = *slot;
            *slot = newValue;
        } else {
            spin_lock_t *slotlock = &PropertyLocks[GOODHASH(slot)];
            _spin_lock(slotlock);
            oldValue = *slot;
            *slot = newValue;        
            _spin_unlock(slotlock);
        }

        objc_release(oldValue);
    }

0

전체 혼란을 단순화하기 위해 뮤텍스 잠금을 이해하자.

이름에 따라 뮤텍스 잠금은 객체의 변경 성을 잠급니다. 따라서 클래스가 객체에 액세스하면 다른 클래스는 동일한 객체에 액세스 할 수 없습니다.

iOS에서는 @sychronisemutex 잠금도 제공합니다. 이제는 FIFO 모드에서 작동하며 동일한 인스턴스를 공유하는 두 클래스의 흐름에 영향을 미치지 않습니다. 그러나 작업이 기본 스레드에있는 경우 UI를 보유하고 성능을 저하시킬 수 있으므로 원자 속성을 사용하여 객체에 액세스하지 마십시오.


-1

원자 : NSLOCK을 사용하여 스레드를 잠그면 스레드 안전성이 보장됩니다.

비 원자 : 스레드 잠금 메커니즘이 없기 때문에 스레드 안전성을 보장하지 않습니다.


-1

원자 속성 :-원자 속성으로 지정된 변수가 하나의 스레드 액세스 만 가지고 스레드 안전하고 성능 관점에서 우수하다는 것을 의미하면 기본 동작을합니다.

비 원자 속성 :-원자 속성이 할당 된 변수는 다중 스레드 액세스가 가능하고 스레드 안전하지 않고 성능 관점에서 느려질 때 기본 동작을 가지며 두 개의 다른 스레드가 동시에 변수에 액세스하려고 할 때 예기치 않은 결과가 발생합니다.

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