Есть ли у Swift модификаторы доступа?

273

В Objective-C данные экземпляра могут быть public, protectedили private. Например:

@interface Foo : NSObject
{
  @public
    int x;
  @protected:
    int y;
  @private:
    int z;
  }
-(int) apple;
-(int) pear;
-(int) banana;
@end

Я не нашел упоминаний о модификаторах доступа в Swift. Можно ли ограничить видимость данных в Swift?

Герго Эрдоси
источник
И я нет. Apple должна, по крайней мере, ввести этикет для рядовых, как в питоне, с префиксом подчеркивания.
Ciantic
Добавлен обновленный ответ для финальной версии Xcode 6.1.1
holroy
Swift 4 обновленный ответ .
Ахмад Ф

Ответы:

419

Начиная с Swift 3.0.1 , существует 4 уровня доступа , описанных ниже от самого высокого (наименее ограничительного) до самого низкого (наиболее ограничительного).


1. openиpublic

Разрешить использование сущности вне определяющего модуля (цели). Вы обычно используете openили publicполучаете доступ при указании открытого интерфейса к платформе.

Однако openдоступ применяется только к классам и членам класса , и он отличается от publicдоступа следующим образом:

  • public классы и члены класса могут быть разделены на подклассы и переопределены только в определяющем модуле (цель).
  • open классы и члены класса могут быть разделены на подклассы и переопределены как внутри, так и вне определяющего модуля (цели).

// First.framework – A.swift

open class A {}

// First.framework – B.swift

public class B: A {} // ok

// Second.framework – C.swift

import First

internal class C: A {} // ok

// Second.framework – D.swift

import First

internal class D: B {} // error: B cannot be subclassed

2. internal

Позволяет использовать сущность внутри определяющего модуля (цели). Обычно вы используете internalдоступ при определении внутренней структуры приложения или инфраструктуры.

// First.framework – A.swift

internal struct A {}

// First.framework – B.swift

A() // ok

// Second.framework – C.swift

import First

A() // error: A is unavailable

3. fileprivate

Ограничивает использование сущности своим определяющим исходным файлом. Обычно вы используете fileprivateдоступ, чтобы скрыть детали реализации определенной части функциональности, когда эти детали используются во всем файле.

// First.framework – A.swift

internal struct A {

    fileprivate static let x: Int

}

A.x // ok

// First.framework – B.swift

A.x // error: x is not available

4. private

Ограничивает использование объекта прилагаемой декларацией. Обычно вы используете privateдоступ, чтобы скрыть детали реализации определенной части функциональности, когда эти детали используются только в пределах одной декларации.

// First.framework – A.swift

internal struct A {

    private static let x: Int

    internal static func doSomethingWithX() {
        x // ok
    }

}

A.x // error: x is unavailable
akashivskyy
источник
37
Может ли кто-нибудь объяснить мне, почему это не имеет большого значения?
Zaky German
15
В ООП всегда есть некоторые методы или переменные, которые должны быть частными или защищенными. Это позволяет реализовать дизайн SOLID , так как большие методы делятся на несколько меньших, каждый из которых несет свою ответственность, которые могут быть переопределены, но для общего пользования должен быть доступен только «основной» метод.
Акашивский
19
Лично мне не нравятся решения, подобные тому, в котором подчеркивание / special-char приводят "частные" методы. Даже если гарантировано, что я для себя буду единственным человеком, который когда-либо будет смотреть на этот код, это делает код более безопасным / менее подверженным ошибкам, потому что компилятор просто не позволит вам делать то, что вы не должны делать. Поэтому я думаю, что они должны как можно быстрее вывести «механизмы контроля доступа», чтобы люди не привыкли к вредным привычкам.
Йонас Эшманн
10
В примечаниях к выпуску бета-версии Xcode 6 говорится: «Управление доступом (общедоступные / закрытые участники) не включено в этом семени. (15747445)»
Мартин Уллрих,
9
@alcalde Идея публичного интерфейса очень ценна. Если вы намереваетесь, что весь код в классе должен находиться внутри функций, которые являются частью общедоступного API, я думаю, что это довольно ограничивает. С другой стороны, наличие указанного открытого API позволяет изменять реализацию (включая использование частных методов), не мешая потребителям. Если кому-то «нужно» использовать метод внутреннего класса, я чувствую, что он неправильно понимает ограничения функциональности класса (или пытается использовать класс с ошибками).
Джинглстхула
26

Свифт 4 / Свифт 5

Как указано в Документации Swift - Контроль доступа , Swift имеет 5 элементов управления доступом :

  • open и public : доступ к ним возможен из сущностей их модуля и из сущностей любого модуля, который импортирует определяющий модуль.

  • внутренняя : доступна только из сущностей их модуля. Это уровень доступа по умолчанию.

  • fileprivate и private : доступ к ним возможен только в ограниченной области, где вы их определяете.



В чем разница между открытым и публичным ?

open аналогичен public в предыдущих версиях Swift, они позволяют классам из других модулей использовать и наследовать их, то есть: они могут быть разделены на подклассы из других модулей. Кроме того, они позволяют членам из других модулей использовать и переопределять их. Та же логика распространяется и на их модули.

public позволяет классам из другого модуля использовать их, но не наследовать их, то есть: они не могут быть разделены на подклассы из других модулей. Кроме того, они позволяют членам из других модулей использовать их, но НЕ отменять их. Для своих модулей они имеют одинаковую логику открытия (они позволяют классам использовать и наследовать их; они позволяют членам использовать и переопределять их).


В чем разница между fileprivate и private ?

fileprivate может быть доступен из всех их файлов.

доступ к private возможен только из их единого объявления и расширений этого объявления, которые находятся в одном файле; Например:

// Declaring "A" class that has the two types of "private" and "fileprivate":
class A {
    private var aPrivate: String?
    fileprivate var aFileprivate: String?

    func accessMySelf() {
        // this works fine
        self.aPrivate = ""
        self.aFileprivate = ""
    }
}

// Declaring "B" for checking the abiltiy of accessing "A" class:
class B {
    func accessA() {
        // create an instance of "A" class
        let aObject = A()

        // Error! this is NOT accessable...
        aObject.aPrivate = "I CANNOT set a value for it!"

        // this works fine
        aObject.aFileprivate = "I CAN set a value for it!"
    }
}



Каковы различия между Swift 3 и Swift 4 Access Control?

Как упомянуто в предложении SE-0169 , единственное уточнение, которое было добавлено в Swift 4, заключается в том, что область частного управления доступом была расширена, чтобы быть доступной из расширений этого объявления в том же файле; Например:

struct MyStruct {
    private let myMessage = "Hello World"
}

extension MyStruct {
    func printMyMessage() {
        print(myMessage)
        // In Swift 3, you will get a compile time error:
        // error: 'myMessage' is inaccessible due to 'private' protection level

        // In Swift 4 it should works fine!
    }
}

Таким образом, нет необходимости объявлять myMessageкак fileprivate доступным во всем файле.

Ахмад Ф
источник
17

Когда говорят о создании «частного метода» в Swift или ObjC (или ruby, java или…), эти методы на самом деле не являются частными. Там нет фактического контроля доступа вокруг них. Любой язык, который предлагает даже небольшой самоанализ, позволяет разработчикам получать эти значения извне класса, если они действительно этого хотят.

То, о чем мы на самом деле говорим, это способ определения общедоступного интерфейса, который просто представляет функциональность, к которой мы стремимся, и «скрывает» все остальное, что мы считаем «приватным».

Механизм Swift для объявления интерфейсов есть protocol, и его можно использовать для этой цели.

protocol MyClass {
  var publicProperty:Int {get set}
  func publicMethod(foo:String)->String
}

class MyClassImplementation : MyClass {
  var publicProperty:Int = 5
  var privateProperty:Int = 8

  func publicMethod(foo:String)->String{
    return privateMethod(foo)
  }

  func privateMethod(foo:String)->String{
    return "Hello \(foo)"
  }
}

Помните, что протоколы являются первоклассными типами и могут использоваться везде, где могут типы. И при использовании таким образом они предоставляют только свои собственные интерфейсы, а не интерфейсы реализующего типа.

Таким образом, до тех пор, пока вы используете MyClassвместо MyClassImplementationтипов параметров и т. Д., Все должно работать:

func breakingAndEntering(foo:MyClass)->String{
  return foo.privateMethod()
  //ERROR: 'MyClass' does not have a member named 'privateMethod'
}

Есть несколько случаев прямого присваивания, когда вы должны явно указывать тип, а не полагаться на Swift, чтобы вывести его, но это вряд ли нарушает условия:

var myClass:MyClass = MyClassImplementation()

Использование протоколов таким образом семантически, достаточно кратко и, на мой взгляд, очень похоже на расширения классов, которые мы использовали для этой цели в ObjC.

jemmons
источник
1
Если протоколы не позволяют нам иметь аргумент по умолчанию, как я могу создать открытый метод с необязательными параметрами, который по-прежнему соответствует протоколу?
bdurao
Я не понимаю, что вы имеете в виду. Следующее создает открытый метод с необязательным параметром. Похоже, проблем нет: gist.github.com/anonymous/17d8d2d25a78644046b6
Джеммонс
По какой-то причине необязательный параметр не работает, как это должно быть в моем проекте, уже пробовал что-то похожее на ваш пример GitHub. Поскольку мы не можем установить параметр по умолчанию в протоколе, я застрял и в итоге задал вопрос. Спасибо за попытку помочь.
Bdurao
Мы все знаем, что все взломано. Нам просто нужен какой-то порядок, зачем нужны модификаторы доступа
canbax
14

Насколько я могу судить, здесь нет ключевых слов "public", "private" или "protected". Это предполагает, что все является публичным.

Однако Apple может ожидать, что люди будут использовать « протоколы » (называемые интерфейсами остальным миром) и шаблон фабричного проектирования, чтобы скрыть детали типа реализации.

Это часто хороший шаблон дизайна для использования в любом случае; поскольку это позволяет вам изменить иерархию классов реализации , сохраняя при этом систему логических типов.

Ян Рингроз
источник
Это хорошо, так как это также уменьшает сцепление и может облегчить тестирование.
Scroog1
4
Это сработало бы лучше, если бы был способ скрыть класс реализации протокола, но, похоже, его нет.
Дэвид Моулз
Может ли кто-нибудь привести иллюстративный пример этого паттерна?
bloudermilk
Ну, этот ответ был действителен в предыдущих версиях Swift, похоже, он больше не действителен :), пожалуйста, проверьте мой ответ .
Ахмад Ф
12

Используя комбинацию протоколов, замыканий и вложенных / внутренних классов, можно прямо сейчас использовать что-то в соответствии с шаблоном модуля, чтобы скрыть информацию в Swift. Это не супер чистый или приятно читать, но это работает.

Пример:

protocol HuhThing {
  var huh: Int { get set }
}

func HuhMaker() -> HuhThing {
   class InnerHuh: HuhThing {
    var innerVal: Int = 0
    var huh: Int {
      get {
        return mysteriousMath(innerVal)
      }

      set {
       innerVal = newValue / 2
      }
    }

    func mysteriousMath(number: Int) -> Int {
      return number * 3 + 2
    }
  }

  return InnerHuh()
}

HuhMaker()
var h = HuhMaker()

h.huh      // 2
h.huh = 32 
h.huh      // 50
h.huh = 39
h.huh      // 59

innerVal и mysteriousMath скрыты здесь от постороннего использования, и попытка проникнуть внутрь объекта может привести к ошибке.

Я только в процессе чтения документов Swift, поэтому, если здесь есть какой-то недостаток, пожалуйста, укажите на это, и я хотел бы знать.

Дэйв Капп
источник
хорошо, я тоже думал об этом решении, но объясните мне, почему я не могу получить доступ к h.huh.innerVal?
Сэм
Swift безопасен от типов, и единственное, что внешний мир знает о h, это то, что он соответствует HuhThing. HuhThing не содержит никакой информации о свойстве, называемом innerVal, поэтому попытка доступа к нему является ошибкой.
Дэйв Капп
8
Все еще доступно: Preflect(h)[0].1.value // 19
Джон Эстропия
2
Отличная находка, Джон - я не осознавал, что задумался. Кажется, превращает объекты в кортежи - есть ли официальная документация по этой функции или другим метапрограммирующим компонентам в Swift? Я посмотрел руководство по языку на iBooks, но не вижу его.
Дэйв Капп
1
@JohnEstropia Я не думаю, что отражение имеет значение. В Java (более зрелый язык), там есть модификаторы доступа, но они не мешают фокусы отражения либо.
11684
10

Начиная с Xcode 6 beta 4, Swift имеет модификаторы доступа. Из примечаний к выпуску:

Swift access control имеет три уровня доступа:

  • частные объекты могут быть доступны только из исходного файла, в котором они определены.
  • внутренние объекты могут быть доступны в любом месте цели, где они определены.
  • общедоступные объекты могут быть доступны из любого места в пределах цели и из любого другого контекста, который импортирует модуль текущей цели.

Неявное значение по умолчанию - это то internal, что в пределах цели приложения вы можете оставить модификаторы доступа отключенными, кроме случаев, когда вы хотите быть более строгими. В целевом фреймворке (например, если вы встраиваете фреймворк для обмена кодом между приложением и общим представлением или расширением представления Today), используйте publicдля обозначения API, который вы хотите предоставить клиентам вашей фреймворка.

rickster
источник
Ну, этот ответ был действителен в предыдущих версиях Swift, похоже, он больше не действителен :), пожалуйста, проверьте мой ответ .
Ахмад Ф
6

Swift 3.0 предоставляет пять различных средств управления доступом:

  1. открыто
  2. общественности
  3. внутренний
  4. fileprivate
  5. частный

Открытый доступ и открытый доступ позволяют использовать сущности в любом исходном файле из их определяющего модуля, а также в исходном файле из другого модуля, который импортирует определяющий модуль. Обычно вы используете открытый или публичный доступ при указании открытого интерфейса к платформе.

Внутренний доступ позволяет использовать сущности в любом исходном файле из их определяющего модуля, но не в любом исходном файле за пределами этого модуля. Обычно вы используете внутренний доступ при определении внутренней структуры приложения или платформы.

Файл-частное доступ к ограничивает использование объекта собственным определяющим исходным файлом. Используйте частный доступ к файлу, чтобы скрыть детали реализации определенной части функциональности, когда эти детали используются во всем файле.

Частный доступ ограничивает использование объекта прилагаемой декларацией. Используйте частный доступ, чтобы скрыть детали реализации определенной части функциональности, когда эти детали используются только в пределах одной декларации.

Открытый доступ - самый высокий (наименее ограничивающий) уровень доступа, а частный доступ - самый низкий (наиболее ограничивающий) уровень доступа.

Уровни доступа по умолчанию

Все сущности в вашем коде (за некоторыми исключениями) имеют уровень доступа по умолчанию внутренний, если вы сами не указали явный уровень доступа. В результате во многих случаях вам не нужно указывать явный уровень доступа в вашем коде.

Информация о выпуске по теме:

Классы, объявленные как публичные, больше не могут быть разделены на подклассы вне своего определяющего модуля, а методы, объявленные как публичные, больше не могут быть переопределены вне своего определяющего модуля. Чтобы разрешить внешнее наследование класса или метод внешней переопределения, объявите их как открытые, что является новым уровнем доступа за пределами public. Импортированные классы и методы Objective-C теперь импортируются как открытые, а не как открытые. Модульные тесты, которые импортируют модуль с использованием импорта @testable, будут по-прежнему иметь возможность создавать подклассы открытых или внутренних классов, а также переопределять публичные или внутренние методы. (SE-0117)

Больше информации и подробностей: Язык программирования Swift (Контроль доступа)

CryingHippo
источник
Ну, этот ответ был действителен в предыдущих версиях Swift, похоже, он больше не действителен :), пожалуйста, проверьте мой ответ .
Ахмад Ф
4

В Beta 6 документация утверждает, что есть три различных модификатора доступа:

  • общественного
  • внутренний
  • Частный

И эти три относятся к классам, протоколам, функциям и свойствам.

public var somePublicVariable = 0
internal let someInternalConstant = 0
private func somePrivateFunction() {}

Для получения дополнительной информации проверьте Контроль доступа .

OliverAssad
источник
Должен быть защищенный модификатор, который облегчает создание классов с большей безопасностью.
Кумар C
Ну, этот ответ был действителен в предыдущих версиях Swift, похоже, он больше не действителен :), пожалуйста, проверьте мой ответ .
Ахмад Ф
2

Теперь в бета-версии 4 они добавили модификаторы доступа в Swift.

из XCode 6 бета 4 Realese отмечает :

Swift access control имеет три уровня доступа:

  • private доступ к сущностям возможен только из исходного файла, в котором они определены.
  • internal к сущностям можно получить доступ в любом месте цели, где они определены.
  • public к сущностям можно получить доступ из любого места в пределах цели и из любого другого контекста, который импортирует модуль текущей цели.

По умолчанию большинство сущностей в исходном файле имеют внутренний доступ. Это позволяет разработчикам приложений в значительной степени игнорировать контроль доступа, в то же время предоставляя разработчикам фреймворка полный контроль над API фреймворка.

Алексей Глобчастый
источник
Можете ли вы опубликовать ссылку на это?
Снеговик
Ну, этот ответ был действителен в предыдущих версиях Swift, похоже, он больше не действителен :), пожалуйста, проверьте мой ответ .
Ахмад Ф
2

Механизмы контроля доступа, представленные в Xcode 6 :

Swift предоставляет три различных уровня доступа для объектов в вашем коде. Эти уровни доступа относятся к исходному файлу, в котором определена сущность, а также к модулю, к которому принадлежит исходный файл.

  • Открытый доступ позволяет использовать сущности в любом исходном файле из их определяющего модуля, а также в исходном файле из другого модуля, который импортирует определяющий модуль. Обычно вы используете публичный доступ при указании открытого интерфейса к платформе.
  • Внутренний доступ позволяет использовать сущности в любом исходном файле из их определяющего модуля, но не в любом исходном файле за пределами этого модуля. Обычно вы используете внутренний доступ при определении внутренней структуры приложения или платформы.
  • Частный доступ ограничивает использование объекта собственным определяющим исходным файлом. Используйте частный доступ, чтобы скрыть детали реализации определенной части функциональности.

Общий доступ - это самый высокий (наименее ограничивающий) уровень доступа, а частный доступ - это самый низкий (или наиболее ограничивающий) уровень доступа.

По умолчанию принимает это внутреннее , и делает так, что не нужно указывать. Также обратите внимание, что частный спецификатор не на уровне класса, а на уровне исходного файла. Это означает, что для того, чтобы части класса были действительно приватными, вам нужно разделить их на отдельный файл. Это также представляет некоторые интересные случаи, касающиеся модульного тестирования ...

Еще одно замечание, о котором я упоминал в приведенной выше ссылке, заключается в том, что вы не можете «обновить» уровень доступа. Если вы что-то делите на подклассы, вы можете ограничить это больше, но не наоборот.

Этот последний бит также влияет на функции, кортежи и, конечно, на другие вещи таким образом, что если, например, функция использует закрытый класс, то недопустимо иметь функцию внутреннюю или открытую , поскольку они могут не иметь доступа к закрытому классу. Это приводит к предупреждению компилятора, и вам нужно переопределить функцию как частную функцию.

holroy
источник
Ну, этот ответ был действителен в предыдущих версиях Swift, похоже, он больше не действителен :), пожалуйста, проверьте мой ответ .
Ахмад Ф
2

Swift 3 и 4 принесли много изменений и для уровней доступа к переменным и методам. Swift 3 и 4 теперь имеют 4 различных уровня доступа, где открытый / публичный доступ - самый высокий (наименее ограничивающий) уровень доступа, а частный доступ - самый низкий (наиболее ограничивающий) уровень доступа:

  • Доступ к закрытым функциям и членам возможен только из самой сущности (struct, class,…) и ее расширений (в Swift 3 расширения также были ограничены)
  • Функции и элементы fileprivate могут быть доступны только из исходного файла, в котором они объявлены.
  • внутренние функции и члены (которые используются по умолчанию, если вы явно не добавляете ключевое слово уровня доступа) могут быть доступны в любом месте цели, где они определены. Вот почему TestTarget не имеет автоматического доступа ко всем источникам, они должны быть помечены как доступные в инспекторе файлов xCode.
  • Открытые или открытые функции и члены могут быть доступны из любой точки цели и из любого другого контекста, который импортирует модуль текущей цели.

Интересный:

Вместо того, чтобы отмечать каждый отдельный метод или элемент как «закрытый», вы можете покрыть некоторые методы (например, обычно вспомогательные функции) в расширении класса / структуры и пометить все расширение как «Частное».

class foo { }

private extension foo {
    func somePrivateHelperFunction01() { }
    func somePrivateHelperFunction02() { }
    func somePrivateHelperFunction03() { }
}

Это может быть хорошей идеей, чтобы получить лучший обслуживаемый код. И вы можете легко переключиться (например, для модульного тестирования) на не приватное, просто изменив одно слово.

Документация Apple

LukeSideWalker
источник
Ну, этот ответ был действителен в предыдущих версиях Swift, похоже, он больше не действителен :), пожалуйста, проверьте мой ответ .
Ахмад Ф
2

Для Свифта 1-3:

Нет, это невозможно. Нет никаких частных / защищенных методов и переменных вообще.

Все публично.

Обновление После Swift 4 возможно увидеть другие ответы в этой теме

Сэм
источник
1
Этот комментарий является точным для текущего семени.
Джеспер
2
Для текущего семени. Это появится в будущем .
Джеспер
1
"public" / "protected" / "private" в настоящее время не существует, но вы можете скрыть вещи, используя замыкания, протоколы и внутренние классы - это делает его чем-то похожим на шаблон модуля, обычно используемый в JavaScript. Пожалуйста, посмотрите мой пример кода в моем ответе здесь для примера того, как это сделать. Если я ошибаюсь относительно того, как это работает, и мой пример неверен, пожалуйста, укажите это, поскольку я все еще учусь. :)
Дэйв Капп
Кажется, это больше не действует :), пожалуйста, проверьте мой ответ .
Ахмад Ф
1

Один из вариантов, который вы можете использовать, - это обернуть создание экземпляра в функцию и предоставить соответствующие методы получения и установки в конструкторе:

class Counter {
    let inc: () -> Int
    let dec: () -> Int

    init(start: Int) {
        var n = start

        inc = { ++n }
        dec = { --n }
    }
}


let c = Counter(start: 10)

c.inc()  // 11
c.inc()  // 12
c.dec()  // 11
Бартош Цехановский
источник
0

Грамматика языка не имеет ключевые слова «общественности», «частные» или «защищенные». Это предполагает, что все является публичным. Конечно, мог бы быть какой-то альтернативный метод указания модификаторов доступа без этих ключевых слов, но я не смог найти его в справочнике по языку.

Scroog1
источник
0

Надеемся сэкономить время для тех, кто хочет что-то похожее на защищенные методы:

Как и в других ответах, swift теперь предоставляет модификатор «private», который определяется по файлам, а не по классам, как, например, в Java или C #. Это означает, что если вам нужны защищенные методы, вы можете сделать это с помощью быстрых приватных методов, если они находятся в одном файле.

  1. Создайте базовый класс для хранения «защищенных» методов (фактически закрытых)
  2. Подкласс этого класса использовать те же методы
  3. В других файлах вы не можете получить доступ к методам базового класса, даже если вы подкласс

например, Файл 1:

class BaseClass {
    private func protectedMethod() {

    }
}

class SubClass : BaseClass {
    func publicMethod() {
        self.protectedMethod()  //this is ok as they are in same file
    }
}

Файл 2:

func test() {
    var a = BaseClass()
    a.protectedMethod() //ERROR


    var b = SubClass()
    b.protectedMethod() //ERROR
}

class SubClass2 : BaseClass {
    func publicMethod() {
        self.protectedMethod() //ERROR
    }

}

james_alvarez
источник
-2

до swift 2.0 было только три уровня доступа [открытый, внутренний, приватный], но в swift 3.0 apple добавили два новых уровня доступа: [Open, fileType], так что теперь в swift 3.0 есть 5 уровней доступа. Здесь я хочу очистить роль из этих двух уровней доступа 1. Открытый: это очень похоже на Public, но единственное отличие состоит в том, что Public может получить доступ к подклассу и переопределить, а уровень Open access не может получить доступ к тому, что это изображение взято с сайта Medium, и это описывает разницу между открытым и публичным доступом

Теперь перейдем ко второму новому уровню доступа 2. filetype - это более крупная версия приватного или меньшего уровня доступа, чем внутренний. FileType может получить доступ к расширенной части [class, struct, enum], а приватный не может получить доступ к расширенной части кода, к которой он может получить доступ только лексическая область действия этого изображения взята с веб-сайта Medium и описывает разницу между fileType и уровнем частного доступа

Дилип Тилония
источник