Biến hàm tĩnh trong Swift


96

Tôi đang cố gắng tìm cách khai báo một biến tĩnh chỉ phạm vi cục bộ cho một hàm trong Swift.

Trong C, nó có thể trông giống như sau:

int foo() {
    static int timesCalled = 0;
    ++timesCalled;
    return timesCalled;
}

Trong Objective-C, về cơ bản nó giống nhau:

- (NSInteger)foo {
    static NSInteger timesCalled = 0;
    ++timesCalled;
    return timesCalled;
}

Nhưng tôi dường như không thể làm bất cứ điều gì như thế này trong Swift. Tôi đã thử khai báo biến theo những cách sau:

static var timesCalledA = 0
var static timesCalledB = 0
var timesCalledC: static Int = 0
var timesCalledD: Int static = 0

Nhưng tất cả đều dẫn đến lỗi.

  • Đầu tiên phàn nàn "Thuộc tính tĩnh chỉ có thể được khai báo trên một kiểu".
  • Lời phàn nàn thứ hai là "Khai báo mong đợi" (ở đâu static) và "Mẫu mong đợi" (ở đâu timesCalledB)
  • Người thứ ba phàn nàn "Các câu lệnh liên tiếp trên một dòng phải được phân tách bằng ';'" (trong khoảng trống giữa dấu hai chấm và static) và "Loại mong đợi" (ở đâu static)
  • Khiếu nại thứ tư "Các câu lệnh liên tiếp trên một dòng phải được phân tách bằng ';'" (trong khoảng trắng giữa Intstatic) và "Khai báo mong đợi" (dưới dấu bằng)

Câu trả lời:


158

Tôi không nghĩ rằng Swift hỗ trợ biến tĩnh mà không gắn nó với một lớp / struct. Hãy thử khai báo một cấu trúc riêng với biến tĩnh.

func foo() -> Int {
    struct Holder {
        static var timesCalled = 0
    }
    Holder.timesCalled += 1
    return Holder.timesCalled
}

  7> foo()
$R0: Int = 1
  8> foo()
$R1: Int = 2
  9> foo()
$R2: Int = 3

Vâng, tôi tiếp tục chơi xung quanh một chút và về cơ bản đây cũng là giải pháp thực sự khó hiểu mà tôi đã nghĩ ra.
nhgrif

17
Được ủng hộ, nhưng tôi buồn vì chúng tôi phải dùng đến điều này.
Tricertops

1
Thuộc tính kiểu và phương thức thuộc về một kiểu (tức là Lớp, Cấu trúc hoặc Enum) và không thể chỉ thuộc về một hàm. Tài liệu Apple về Thuộc tính Loại . @Tricertops. Một cách khác để thực hiện điều này là đặt hàm "foo" trong một lớp, tạo một thuộc tính kiểu cho lớp đó và sử dụng nó bên trong hàm.
NSCoder

6
@NSCoder Nhưng có thể khai báo struct Holder {…}bên trong nhiều hàm và chúng sẽ không xung đột với nhau. Swift có thể hỗ trợ static letmà không có bảng structsoạn sẵn này .
Tricertops

1
@Honey Tôi xin lỗi nhưng tôi không thể tìm thấy câu trả lời cập nhật khác?
Bryan Chen

23

Giải pháp khác

func makeIncrementerClosure() -> () -> Int {
    var timesCalled = 0
    func incrementer() -> Int {
        timesCalled += 1
        return timesCalled
    }
    return incrementer
}

let foo = makeIncrementerClosure()
foo()  // returns 1
foo()  // returns 2

3
đây là một cách điển hình javascript để làm điều đó
Bryan Chen

1
Nhưng nếu tôi gọi lại ba (), hàm bên trong trả về 1 trong lần gọi đầu tiên. Điều này khác với một biến tĩnh.
nhgrif

2
Điều này cũng được dạy trong tài liệu của Apple tại đây: developer.apple.com/library/ios/documentation/Swift/Conceptual/… Có vẻ là giải pháp tốt nhất chỉ để phù hợp với "lập trình chức năng", nhưng có các giải pháp khác như tốt. Đây nên là câu trả lời được chấp nhận.
datWooWoo

1
Tôi xin lỗi, nhưng đây là một vụ hack xấu xí, tăng thêm độ phức tạp cho cùng một vấn đề. Ý bạn là sao? Trong trường hợp đó, tôi thích một thuộc tính lớp đơn giản hơn. Câu trả lời của @Brian Chen là câu trả lời gần nhất mà bạn có thể nhận được. Tôi sử dụng câu trả lời của anh ấy cho một loại giải pháp flipflop. Daniel có lẽ là người tuân thủ tốt nhất các quy tắc lập trình Swift của Apple.
AndaluZ

1
Tôi đặc biệt thích giải pháp này. Đây là một ví dụ hoàn hảo về việc sử dụng một hàm bậc cao hơn để đạt được kết quả tương tự như một biến tĩnh bên trong phạm vi của một hàm. Các vars chức năng tĩnh không được hỗ trợ nguyên bản trong Swift vì những lý do chính đáng. Đó là sự phát triển tự nhiên của lập trình. Cố gắng viết mã theo những cách cũ cần có hack. Theo ý kiến ​​của tôi, việc thêm một kiểu dữ liệu lồng nhau bổ sung thay vì sử dụng khả năng bắt biến làm giảm khả năng đọc mã.
nstein

18

Swift 1.2 với Xcode 6.3 hiện hỗ trợ tĩnh như mong đợi. Từ ghi chú phát hành Xcode 6.3 beta:

Các phương thức và thuộc tính “tĩnh” hiện được cho phép trong các lớp (như một bí danh cho “lớp cuối cùng”). Bây giờ bạn được phép khai báo các thuộc tính được lưu trữ tĩnh trong các lớp, các thuộc tính này có lưu trữ toàn cục và được khởi tạo một cách lười biếng trong lần truy cập đầu tiên (như các biến toàn cục). Các giao thức hiện khai báo các yêu cầu kiểu là các yêu cầu “tĩnh” thay vì khai báo chúng là các yêu cầu “lớp”. (17198298)

Có vẻ như các hàm không thể chứa các khai báo tĩnh (như đã hỏi trong câu hỏi). Thay vào đó, việc khai báo phải được thực hiện ở cấp độ lớp.

Ví dụ đơn giản cho thấy một thuộc tính tĩnh được tăng lên bên trong một hàm lớp (còn gọi là tĩnh), mặc dù một hàm lớp không bắt buộc:

class StaticThing
{
    static var timesCalled = 0

    class func doSomething()
    {
        timesCalled++

        println(timesCalled)
    }
}

StaticThing.doSomething()
StaticThing.doSomething()
StaticThing.doSomething()

Đầu ra:

1
2
3

1
Tôi nghi ngờ sự khác biệt về ý nghĩa này staticcó thể do Apple cố ý, mặc dù người ta luôn hoan nghênh việc gửi lỗi để yêu cầu thay đổi. Trong C, staticgiới hạn việc lưu trữ một biến trong phạm vi tệp nguồn (không phải lúc nào cũng giống với phạm vi lớp), trong khi vị trí của khai báo biến xác định phạm vi từ vựng (tức là toàn cục so với trong hàm so với nhiều lồng nhau {}). Trong Swift, phạm vi lưu trữ luôn tuân theo phạm vi từ vựng, vì vậy bạn không thể có một biến từ vựng cho một hàm và có khả năng lưu trữ toàn cục.
rickster

5
Daniel, điều này thực sự khác một cách tinh tế (nhưng quan trọng) với những gì câu hỏi đặt ra. Tôi đánh giá cao câu trả lời mặc dù. @rickster Tôi hiểu bạn đang nói gì và nghĩ rằng bình luận của bạn có thể được mở rộng thành một câu trả lời tốt cho câu hỏi này.
nhgrif 11/02/15

@nhgrif Đúng, tôi đã chỉ ra trong câu trả lời rằng điều này không giải quyết được câu hỏi cụ thể. Tôi chỉ nghĩ rằng những thay đổi trong Swift 1.2 giải quyết nhu cầu cốt lõi cho trường hợp sử dụng này (chắc chắn là một câu chuyện hay hơn Swift 1.2 trước đó). Nhưng có vẻ như điều quan trọng là bạn phải có phạm vi biến cho hàm - điều này hiện tại là không thể.
Daniel

@rickster trong CI nghĩ rằng tĩnh luôn được lưu trữ trên toàn cầu. Tôi không chắc lắm. Tôi nghĩ đó là vấn đề mà Apple đang cố gắng giải quyết ở đây. Trong nhanh chóng, nó bây giờ là lúc nào cũng giải nghĩa từ vựng và lưu trữ scoped đến lớp
BTRUE

@nhgrif Với nhận xét trước đây của tôi đã nói, tôi nghĩ câu trả lời của Daniel thực sự nên là câu trả lời được chấp nhận, bởi vì mặc dù bạn có thể khai báo từ vựng một var tĩnh trong một hàm trong objc, nhưng nó không có phạm vi ở đó, có tác dụng giống như sử dụng Loại tĩnh tài sản nhanh chóng. điểm khác biệt duy nhất là, điểm khai báo nhanh mang tính mô tả nhiều hơn và không gây hiểu lầm về phạm vi của biến là gì.
BTRUE

0

Giải pháp khác

class Myclass {
    static var timesCalled = 0
    func foo() -> Int {
        Myclass.timesCalled += 1
        return Myclass.timesCalled
    }
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.