계산 된 읽기 전용 속성 대 Swift의 함수


98

Swift WWDC 소개 세션에서 읽기 전용 속성 description이 표시됩니다.

class Vehicle {
    var numberOfWheels = 0
    var description: String {
        return "\(numberOfWheels) wheels"
    }
}

let vehicle = Vehicle()
println(vehicle.description)

대신 방법을 사용하는 것보다 위의 접근 방식을 선택하는 데 의미가 있습니까?

class Vehicle {
    var numberOfWheels = 0
    func description() -> String {
        return "\(numberOfWheels) wheels"
    }
}

let vehicle = Vehicle()
println(vehicle.description())

읽기 전용 계산 속성을 선택하는 가장 분명한 이유는 다음과 같습니다.

  • 의미론 -이 예 description에서는 클래스가 수행하는 작업이 아니라 클래스의 속성 인 것이 합리적입니다 .
  • 간결성 / 명확성 -값을 가져올 때 빈 괄호를 사용할 필요가 없습니다.

분명히 위의 예는 지나치게 간단하지만 하나를 선택해야하는 다른 좋은 이유가 있습니까? 예를 들어, 어떤 기능을 사용할지 결정하는 데 도움이되는 기능이나 속성이 있습니까?


NB 언뜻보기에 이것은 매우 일반적인 OOP 질문처럼 보이지만,이 언어를 사용할 때 모범 사례를 안내 할 스위프트 관련 기능을 알고 싶습니다.


1
(204) 세션보기 - "사용하지 않을 @property에 때"그것은 몇 가지 팁이있다
Kostiantyn 코발

4
잠깐, 읽기 전용 속성을 수행하고 get {}? 몰랐습니다, 감사합니다!
Dan Rosenstark 2015

WWDC14 세션 204 여기 (비디오 및 슬라이드), 찾을 수 있습니다 developer.apple.com/videos/play/wwdc2014/204
user3207158

답변:


53

그것은 대부분 스타일의 문제인 것 같습니다. 나는 속성 을 사용하는 것을 강력히 선호 합니다 : 속성; 얻을 수있는 간단한 값을 의미합니다. 나는 실제 작업을 할 때 기능 (또는 방법)을 사용 합니다 . 디스크 나 데이터베이스에서 무언가를 계산하거나 읽어야 할 수도 있습니다.이 경우 간단한 값만 반환되는 경우에도 함수를 사용합니다. 이렇게하면 호출이 저렴한 지 (속성) 비용이 많이 드는지 (함수) 쉽게 확인할 수 있습니다.

Apple이 몇 가지 Swift 코딩 규칙을 게시하면 더 명확해질 것입니다.


12

글쎄, 당신은 Kotlin의 조언 https://kotlinlang.org/docs/reference/coding-conventions.html#functions-vs-properties를 적용 할 수 있습니다 .

경우에 따라 인수가없는 함수는 읽기 전용 속성과 교환 할 수 있습니다. 의미는 비슷하지만 서로 선호하는시기에 대한 몇 가지 스타일 규칙이 있습니다.

기본 알고리즘이 다음과 같은 경우 함수보다 속성을 선호합니다.

  • 던지지 않는다
  • 복잡성은 계산하기가 저렴합니다 (또는 첫 번째 실행에서 계산 됨).
  • 호출에 대해 동일한 결과를 반환합니다.

1
"has a O (1)"제안은 더 이상 해당 조언에 포함되지 않습니다.
David Pettigrew

Kotlin의 변경 사항을 반영하도록 수정되었습니다.
Carsten Hagemann

11

일반적으로 계산 된 속성 대 메소드에 대한 질문은 어렵고 주관적이지만 현재 Swift의 경우 속성보다 메소드를 선호하는 중요한 주장이 하나 있습니다. 속성에 맞지 않는 순수한 함수로 Swift의 메서드를 사용할 수 있습니다 (Swift 2.0 베타 기준). 이것은 기능적 구성에 참여할 수 있기 때문에 메소드를 훨씬 더 강력하고 유용하게 만듭니다.

func fflat<A, R>(f: (A) -> () -> (R)) -> (A) -> (R) {
    return { f($0)() }
}

func fnot<A>(f: (A) -> Bool) -> (A) -> (Bool) {
    return { !f($0) }
}

extension String {
    func isEmptyAsFunc() -> Bool {
        return isEmpty
    }
}

let strings = ["Hello", "", "world"]

strings.filter(fnot(fflat(String.isEmptyAsFunc)))

1
strings.filter {! $ (0) .isEmpty}-동일한 결과를 반환합니다. Array.filter ()의 apple 문서에서 수정 된 샘플입니다. 그리고 훨씬 더 이해하기 쉽습니다.
poGUIst

7

런타임이 동일하기 때문에이 질문은 Objective-C에도 적용됩니다. 나는 당신이 얻는 속성으로

  • 하위 클래스에 setter를 추가하여 속성을 만들 가능성 readwrite
  • didSet변경 알림 을 위해 KVO /를 사용하는 기능
  • 보다 일반적으로 키 경로를 예상하는 메소드에 속성을 전달할 수 있습니다 (예 : 가져 오기 요청 정렬)

Swift에 특정한 것에 관해서는 제가 가진 유일한 예 @lazy는 속성에 사용할 수 있다는 것 입니다.


7

차이점이 있습니다. 속성을 사용하면 결국이를 재정의하고 하위 클래스에서 읽기 / 쓰기로 만들 수 있습니다.


9
함수를 재정의 할 수도 있습니다. 또는 쓰기 능력을 제공하기 위해 setter를 추가합니다.
Johannes Fahrenkrug 2014-06-05

기본 클래스가 이름을 함수로 정의 할 때 setter를 추가하거나 저장된 속성을 정의 할 수 있습니다. 확실히 속성을 정의하면 할 수 있지만 (정확히 내 요점) 함수를 정의하면 할 수 있다고 생각하지 않습니다.
Analog File

Swift에 개인 속성이 있으면 (여기 stackoverflow.com/a/24012515/171933 참조 ) 하위 클래스에 setter 함수를 추가하여 해당 개인 속성을 설정할 수 있습니다. getter 함수를 "name"이라고하면 setter는 "setName"이되므로 이름 충돌이 발생하지 않습니다.
Johannes Fahrenkrug

이미 할 수 있습니다 (차이점은 지원을 위해 사용하는 저장된 속성이 공개된다는 것입니다). 그러나 OP는 읽기 전용 속성이나 기본 함수를 선언하는 것 사이에 차이점이 있는지 물었습니다. 읽기 전용 속성을 선언하면 파생 클래스에서 읽기-쓰기로 만들 수 있습니다. 향후 파생 클래스에 대해 알지 못하는 상태 에서 기본 클래스에 willSet및 추가하는 확장 은 재정의 된 속성의 변경 사항을 감지 할 수 있습니다. 하지만 기능으로는 그런 일을 할 수 없다고 생각합니다. didSet
Analog File

setter를 추가하기 위해 읽기 전용 속성을 어떻게 재정의 할 수 있습니까? 감사. "하위 클래스 속성 재정의에 getter와 setter를 모두 제공하여 상속 된 읽기 전용 속성을 읽기-쓰기 속성으로 표시 할 수 있습니다."라는 문서에서 이것을 볼 수 있지만 ... setter는 어떤 변수에 씁니까?
Dan Rosenstark 15.11.02

5

읽기 전용의 경우, 선언을 삭제 하면 인스턴스 의 상태 를 구성하는 수량과 단순히 함수 인 수량 간의 구분이 모호해 지기 때문에 계산 된 속성이 메서드와 의미 적으로 동일하다고 간주 되어서는 안됩니다 . 상태. 호출 사이트에서 타이핑 을 저장 하지만 코드의 명확성을 잃을 위험이 있습니다.func()

간단한 예로 다음 벡터 유형을 고려하십시오.

struct Vector {
    let x, y: Double
    func length() -> Double {
        return sqrt(x*x + y*y)
    }
}

길이를 메서드로 선언하면 xand에 의존하는 상태의 함수임을 분명히 알 수 y있습니다.

반면 length에 계산 된 속성 으로 표현한다면

struct VectorWithLengthAsProperty {
    let x, y: Double
    var length: Double {
        return sqrt(x*x + y*y)
    }
}

당신의 인스턴스에 IDE에서 탭 완성 점 때 다음 VectorWithLengthAsProperty, 그것은 것처럼 보일 것이다 x, y, length개념적으로 잘못 동등한에 등록했다.


5
이것은 흥미롭지 만이 원칙을 따를 때 계산 된 읽기 전용 속성 사용되는 예를 제공 수 있습니까? 내가 틀렸을 수도 있지만 , 정의에 따라 계산 된 읽기 전용 속성은 상태를 포함하지 않기 때문에 절대 사용 해서는 안된다고 주장하는 것 같습니다 .
Stuart

2

일반 함수보다 계산 된 속성을 선호하는 상황이 있습니다. 예 : 사람의 전체 이름을 반환합니다. 이름과 성을 이미 알고 있습니다. 따라서 실제로 fullName속성은 함수가 아닌 속성입니다. 이 경우 계산 된 속성입니다 (전체 이름을 설정할 수 없기 때문에 이름과 성을 사용하여 추출 할 수 있습니다)

class Person{
    let firstName: String
    let lastName: String
    init(firstName: String, lastName: String){
        self.firstName = firstName
        self.lastName = lastName
    }
    var fullName :String{
        return firstName+" "+lastName
    }
}
let william = Person(firstName: "William", lastName: "Kinaan")
william.fullName //William Kinaan

1

성능 측면에서는 차이가없는 것 같습니다. 벤치 마크 결과에서 볼 수 있습니다.

요점

main.swift 코드 스 니펫 :

import Foundation

class MyClass {
    var prop: Int {
        return 88
    }

    func foo() -> Int {
        return 88
    }
}

func test(times: u_long) {
    func testProp(times: u_long) -> TimeInterval {
        let myClass = MyClass()
        let starting = Date()
        for _ in 0...times {
            _ = myClass.prop
        }
        let ending = Date()
        return ending.timeIntervalSince(starting)
    }


    func testFunc(times: u_long) -> TimeInterval {
        let myClass = MyClass()
        let starting = Date()
        for _ in 0...times {
            _ = myClass.prop
        }
        let ending = Date()
        return ending.timeIntervalSince(starting)
    }

    print("prop: \(testProp(times: times))")
    print("func: \(testFunc(times: times))")
}

test(times: 100000)
test(times: 1000000)
test(times: 10000000)
test(times: 100000000)

산출:

prop: 0.0380070209503174 func: 0.0350250005722046 prop: 0.371925950050354 func: 0.363085985183716 prop: 3.4023300409317 func: 3.38373708724976 prop: 33.5842199325562 func: 34.8433820009232 Program ended with exit code: 0

차트에서 :

기준


2
Date()운영 체제에 의해 자동 업데이트되는 컴퓨터 시계를 사용하기 때문에 벤치 마크에 적합하지 않습니다. mach_absolute_time더 신뢰할 수있는 결과를 얻을 수 있습니다.
Cristik

1

의미 상으로 말하면 계산 된 속성은 객체의 고유 상태와 밀접하게 결합되어야합니다. 다른 속성이 변경되지 않으면 계산 된 속성을 다른 시간에 쿼리하면 동일한 출력을 제공해야합니다 (== 또는 ===를 통해 비교 가능). 그 객체에 대한 순수 함수를 호출하는 것입니다.

반면에 메서드는 Swift가 함수를 순수로 표시하는 방법이 없기 때문에 항상 동일한 결과를 얻지 못할 수 있다는 가정하에 기본 제공됩니다. 또한 OOP의 메서드는 작업으로 간주되므로 실행하면 부작용이 발생할 수 있습니다. 메서드에 부작용이 없으면 계산 된 속성으로 안전하게 변환 할 수 있습니다.

위의 두 문장은 순전히 의미 론적 관점에서 나온 것입니다. 계산 된 속성이 우리가 예상하지 못한 부작용을 갖고 있고 메서드가 순수 할 수도 있기 때문입니다.


0

역사적으로 설명은 NSObject의 속성이며 많은 사람들이 Swift에서도 동일하게 계속 될 것으로 예상합니다. 그 뒤에 괄호를 추가하면 혼란을 더할뿐입니다.

편집 : 격렬한 반대 투표 후 뭔가 명확히해야합니다. 점 구문을 통해 액세스하면 속성으로 간주 될 수 있습니다. 내부에 무엇이 있는지는 중요하지 않습니다. 점 구문으로 일반적인 메서드에 액세스 할 수 없습니다.

게다가이 속성을 호출하는 것은 Swift의 경우처럼 추가 괄호가 필요하지 않아 혼란을 초래할 수 있습니다.


1
실제로 이것은 올바르지 않습니다 . 프로토콜 에 description필요한 방법NSObject 이므로 객관적인 C에서 [myObject description]. 어쨌든 속성 description은 단순히 인위적인 예였습니다. 사용자 지정 속성 / 함수에 적용되는보다 일반적인 대답을 찾고 있습니다.
Stuart

1
설명해 주셔서 감사합니다. 나는 당신의 추론을 이해하지만 값을 반환하는 매개 변수가없는 obj-c 메서드가 속성으로 간주 될 수 있다는 귀하의 진술에 완전히 동의하는지 확신하지 못합니다. 지금은 내 반대표를 철회 할 것이지만이 답변은 이미 질문에서 언급 한 '의미 론적'이유를 설명하고 있다고 생각하며 교차 언어 일관성도 여기서도 문제가되지 않습니다.
Stuart
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.