iOS Development Guide

Actor (3) - protocol conformance와 isolated parameter

ZeddiOS의 Swift ) Actor (3) 글을 바탕으로, actor가 프로토콜을 채택할 때 생기는 제약과 `isolated` 파라미터를 이용해 actor 파라미터의 상태를 함수/프로퍼티 본문에서 직접 다루는 방법을 정리한다.

Confirmed from source Inference clearly labeled Knowledge item #76
학습 날짜

2026-04-09

Why This Work Exists

Actor를 실제 타입처럼 쓰다 보면 곧 프로토콜 채택과 utility 함수 작성 문제가 나온다. 이 문서는 “왜 Hashable이 막히는가”, “왜 `isolated` 파라미터를 쓰는가”를 한 번에 정리하기 위한 3편이다.

Scope / Non-scope

  • Hashable/Equatable 채택 시 차이를 설명한다.
  • `hash(into:)`가 왜 막히는지 정리한다.
  • `isolated` 파라미터가 왜 필요한지 설명한다.
  • distributed actor나 custom executor는 다루지 않는다.

As-is

흔한 오해는 actor 내부에 메서드를 정의했으니 프로토콜 요구사항도 다 자동으로 만족할 수 있다는 생각이다. 하지만 protocol requirement의 호출 방식과 actor isolation 규칙이 충돌하는 경우가 있다.

To-be

더 정확한 표현은 이렇다. 일반 actor 메서드는 actor-isolated여도 괜찮지만, protocol requirement가 nonisolated 형태를 기대하면 그대로는 채택할 수 없다.

What The Developer Must Understand Next

핵심 요약

`actor 안에 정의했다`가 중요한 게 아니라, `그 메서드가 어떤 호출 계약을 만족해야 하느냐`가 중요하다.

API / Data Contract

  • 일반 actor 메서드는 actor-isolated여도 문제 없다.
  • `Hashable.hash(into:)`는 non-async 프로토콜 requirement라 actor-isolated 인스턴스 메서드와 충돌할 수 있다.
  • `isolated` 파라미터를 붙이면 함수 전체가 그 actor의 isolation 안에서 실행되는 것처럼 취급된다.
  • 따라서 본문에서는 해당 actor의 isolated 프로퍼티와 메서드를 `await` 없이 직접 다룰 수 있다.

Risks And Decisions Needed

  • `hash도 actor 내부니까 deposit과 똑같다`라고 쓰면 틀리다. protocol requirement인지가 핵심이다.
  • `isolated`는 원래 접근 불가능한 actor 상태를 직접 다루기 위해 붙이는 것이지, 금지돼서 우회하는 표식이 아니다.
  • `isolated` 파라미터를 붙였다고 호출 쪽의 actor 경계 비용이 사라지는 것은 아니다. 보통 호출 시에는 여전히 async 문맥이 필요하다.

Class Diagram

classDiagram class BankAccount { +let accountNumber +var balance +deposit() +hash(into:) } class HashableRequirement { +hash(into:) } class IsolatedParameterFunction { +deposit(amount:to:) } BankAccount ..|> HashableRequirement : may conflict IsolatedParameterFunction --> BankAccount : runs in isolation context

Sequence Diagram

sequenceDiagram participant Caller participant Account as BankAccount Actor participant Helper as isolated parameter function Caller->>Helper: await deposit(amount, to: account) Helper->>Account: enters account isolation Helper->>Account: balance += amount Account-->>Caller: complete

Flowchart

flowchart LR A["Need to access actor parameter state"] --> B{"Inside actor method of same actor?"} B -->|"Yes"| C["Direct access"] B -->|"No"| D{"Can rewrite as isolated parameter?"} D -->|"Yes"| E["Use isolated parameter"] D -->|"No"| F["Use await actor method/property access"]

Detailed Reading: Why Hashable Fails

`deposit()` 같은 메서드는 그냥 actor의 일반 인스턴스 메서드다. 따라서 actor isolation 규칙 안에서만 호출되면 문제가 없다.

하지만 `hash(into:)`는 `Hashable` 프로토콜 requirement다. Swift는 이 requirement를 일반 동기 메서드 형태로 만족하기를 기대하는데, actor의 인스턴스 메서드는 기본적으로 actor-isolated다.

그래서 `hash(into:)`를 actor-isolated 인스턴스 메서드로 두면, `actor-isolated instance method 'hash(into:)' cannot be used to satisfy a protocol requirement` 같은 제약이 발생한다. 즉 핵심은 “내부에 정의했느냐”가 아니라 “프로토콜 요구사항이 어떤 호출 계약을 요구하느냐”다.

Why Equatable Can Still Work

글 문맥에서는 `==` 비교는 `static` 메서드로 작성하고, 비교 기준을 immutable한 `let accountNumber` 같은 값으로 제한하면 `Equatable`은 채택할 수 있음을 보여 준다.

반면 `Hashable`의 `hash(into:)`는 인스턴스 메서드 requirement라서 상황이 다르다. 결국 actor의 API를 어디까지 일반 value type처럼 다룰 수 있는지에 제약이 생긴다.

Detailed Reading: Why isolated Parameter Exists

일반 함수나 computed property는 원래 actor 바깥 문맥이다. 그래서 actor 파라미터를 그냥 받으면 그 actor의 isolated 상태를 본문에서 직접 읽거나 수정할 수 없다.

func deposit(amount: Double, to account: BankAccount) {
    account.balance = account.balance + amount // error
}

여기서 `account`를 `isolated`로 표시하면 함수 전체가 `account` actor의 isolation context 안에서 실행되는 것처럼 취급된다. 그러면 본문에서 `account.balance`를 직접 쓸 수 있다.

func deposit(amount: Double, to account: isolated BankAccount) {
    assert(amount >= 0)
    account.balance = account.balance + amount
}

즉 정리는 이렇다. “원래는 직접 못 쓰기 때문에, actor 파라미터 상태를 함수 본문에서 직접 다루고 싶어서 `isolated`를 붙인다.”

Practical Conclusion

  • actor 내부 일반 메서드와 protocol requirement 메서드는 같은 내부 정의처럼 보여도 제약이 다를 수 있다.
  • `isolated`는 함수 본문에서 actor 파라미터의 상태를 직접 쓸 수 있게 만드는 도구다.
  • 호출하는 쪽에서는 여전히 actor 경계를 넘는 호출로 보통 async 문맥이 필요하다.
  • 즉 `isolated`는 구현 내부 접근성을 바꾸는 것이고, 호출 계약 전체를 동기식으로 바꾸는 것은 아니다.

QA Checklist

  • `hash도 내부니까 deposit과 같다`라고 쓰지 않았는지 확인한다.
  • `isolated 파라미터를 쓰면 안 되기 때문에 붙인다`처럼 반대로 쓰지 않았는지 확인한다.
  • `원래는 직접 못 쓰기 때문에 isolated를 붙인다`로 정리했는지 확인한다.
  • `isolated`와 호출 시 `await`의 역할을 구분했는지 확인한다.

Operations / Rollout Checklist

  • 이 문서는 Actor 시리즈 3편으로 유지한다.
  • 후속 문서에서는 `nonisolated`, protocol conformance 확장, module boundary를 이어서 정리한다.
  • 인덱스 HTML에 시리즈 순서를 계속 보이게 유지한다.