Swift 4 Eklentiler (Extensions) & Protokoller #23

Özcan Akkoca
Etiya
Published in
14 min readJun 29, 2017

--

Bilindiği gibi nesne yönelimli programlama tekniğinde bir sınıfa onu değiştirmeden eleman ekleme işlemi türetme yoluyla yapılmaktadır. Ancak türetme işleminin belli bir kod maaliyeti vardır. Ayrıca türetme işlemiyle yeni bir sınıf da oluşturulmaktadır. Programa yeni bir sınıfın eklenmesi bazı durumlar için algısal karmaşıklığı artırıcı bir yük oluşturabilmektedir. (Ayrıca Swift’te yapıların ve enum türlerinin türetmeye kapalı olduğunu da anımsayınız) İşte eklentiler maliyetsiz olarak mevcut bir sınıf, yapı ya da enum türüne eleman eklemekte kullanılır.

Eklenti bildiriminin genel biçimi şöyledir:

extension <isim> {

//…

}

Burada isim eklenti yapılacal sınıf, yapı ya da enum’un ismidir.

Eklentiler statik olan ya da olmayan metotlara ve hesaplanmış property’lere sahip olabilirler ancak depolanmış property’lere (yani veri elemanlarına) sahip olamazlar. Eklentiler içerisinde asıl sınıfın, yapının ya da enum’un tüm elemanları doğrudan kullanılabilmektedir. Örneğin:

extension Double {

var mm: Double { return self / 1000}

var cm: Double { return self / 100 }

var m: Double { return self }

var km: Double { return self * 1000 }

}

var distance = 3.4.km

print(distance)

var result = 3.km + 400.m

print(result)

Anahtar Notlar: Yukarıdaki örnekte 3.km ifadesindeki 3 Double derleyici tarafından Double olarak ele alınmaktadır. Halbuki resmi dokümanlarında bunu destekleyecek resmi bir kural göze çarpmamaktadır. Fakat yapılan denemelerde hem Double hem de Int yapıları için aynı eklenti bulundurulduğunda 3.km ifadesinde Int eklentisindeki km property’sinin seçildiği görülmektedir.

Örneğin:

struct Complex {

var real: Double

var imag: Double

init()

{

real = 0

imag = 0

}

init(_ real: Double, _ imag: Double)

{

self.real = real

self.imag = imag

}

}

extension Complex {

static func Add(z1: Complex, _ z2: Complex) -> Complex

{

var result = Complex()

result.real = z1.real + z2.real

result.imag = z1.imag + z2.imag

return result

}

var description: String {

return real.description + “ + “ + imag.description + “i”

}

}

var z1 = Complex(3, 2)

var z2 = Complex(5, 3)

var result:Complex

result = Complex.Add(z1, z2)

print(result.description)

Eklentiler init metotları içerebilir. Ancak eklenti bir sınıf için oluşturulmuşsa eklentideki init medodu “designated” olamaz, “convenience” olmak zorundadır. Anımsanacağı gibi “designated” ve “convenience” init metotları sınıflar için söz konusudur. Eklentiler yapılar için oluşturulmuşsa normal init metotlarına sahip olabilirler. Örneğin:

struct Complex {

var real: Double

var imag: Double

init()

{

real = 0

imag = 0

}

init(_ real: Double, _ imag: Double)

{

self.real = real

self.imag = imag

}

}

extension Complex {

init(_ real: Double)

{

self.real = real

self.imag = 0

}

static func Add(z1: Complex, _ z2: Complex) -> Complex

{

var result = Complex()

result.real = z1.real + z2.real

result.imag = z1.imag + z2.imag

return result

}

var description: String {

var result = real.description

if imag != 0 {

result += “ + “ + imag.description + “i”

}

return result

}

}

var z = Complex(10)

print(z.description)

Eğer yukarıdaki Complex türü bir sınıf olarak düzenlenseydi eklentideki init metodu “convenience” olmak zorunda olurdu:

class Complex {

var real: Double

var imag: Double

init()

{

real = 0

imag = 0

}

init(_ real: Double, _ imag: Double)

{

self.real = real

self.imag = imag

}

}

extension Complex {

convenience init(_ real: Double)

{

self.init(real, 0)

}

static func Add(z1: Complex, _ z2: Complex) -> Complex

{

let result = Complex()

result.real = z1.real + z2.real

result.imag = z1.imag + z2.imag

return result

}

var description: String {

var result = real.description

if imag != 0 {

result += “ + “ + imag.description + “i”

}

return result

}

}

var z = Complex(10)

print(z.description)

Örneğin:

extension Int {

func repeatition(f: (Int)->Void)

{

for i in 0..<self {

f(i)

}

}

}

10.repeatition({i in print(i)})

Eklentiler subscrip elemanlara da sahip olabilir. Örneğin:

extension Int {

subscript(index: Int) -> Int {

var exp = 1

for var i = 0; i < index; ++i {

exp *= 10

}

return self / exp % 10

}

}

print(1234567[5])

Protokoller

Protokol terimi Objective-C’den aktarılmıştır. Protokoller Java ve C#’taki arayüzlere (interfaces) karşılık gelmektedir. Bilindiği gibi arayüzler çoklu türetmenin bazı avantajlarını dile sokmak düşünülmüş türlerdir. C++’ta zaten çoklu türetme olduğu için orada arayüzlere ayrıca gereksinim duyulmamıştır.

Protokol bildiriminin genel biçimi şöyledir:

protocol <isim> {

//…

}

Bir protokol depolanmış property (yani veri elemanı) içeremez. Protokoller metotlara ve hesaplanmış property’lere sahip olabilir. Protokol elemanları gövde içeremez. Örneğin:

protocol Test {

func foo(a: Int) -> Int

func bar()

}

Bir sınıf, yapı ya da enum protokolleri destekleyebilir. Bunun için bildirimde sınıf, yapı ya da enum isminden sonra ‘:’ atomunu protokol listesi izlemelidir. Görüldüğü gibi protokollerin desteklenme sentaksı türetme sentaksına benzemektedir. Örneğin:

class Sample : Test {

//…

}

Anahtar Notlar: Swift terminolojisinde protokolü desteklemek İngilizce “conform” sözcüğü ile ifade edilmektedir. Anımsanacağı gibi aynı kavram Java ve C#’ta “implement” sözcüğüyle ifade ediliyordu. “Conform” sözcüğü İngilizce “uymak (özellikle kurallara vs.)” anlamanına gelmektedir. Ancak biz kursumuzda falanca sınıfın ya da yapının “protokole uyması” yerine falanca sınıfın ya da yapının ilgili protokolü desteklemesi biçiminde ifade kullanacağız.

Bir sınıf, yapı ya da enum bir protokü destekleyiyorsa o protokülün bütün eelemanlarını bildirmelidir. Yani örneğin protokolde bir metot varsa o protokolü destekleyen sınıf, yapı ya da enum o metodu gövdesiyle bildirmek zorundadır. Örneğin:

protocol Test {

func foo(a: Int) -> Int

func bar()

}

class Sample : Test {

func foo(a: Int) -> Int {

return a * a

}

func bar()

{

print(“this is a test”)

}

//…

}

Protokoldeki metotlar desteklenirken onların isimleri, parametre türleri, parametrelerin dışsal isimleri ve geri dönüş değerleri aynı biçimde olmak zorundadır.

Bir sınıf, yapı ya da enum birden fazla protokolü destekleyebilir. Bunların bildirimdeki yazım sırasının hiçbir önemi yoktur. Örneğin:

protocol A {

func foo()

}

protocol B {

func bar()

}

class Sample : A, B {

func foo()

{

//…

}

func bar()

{

//…

}

//…

}

Eğer bir sınıf hem başka bir sınıftan türetilmişse hem de birtakım protoklleri de destekliyorsa sınıfın taban listesinde (yani ‘:’ atomuyla belirtilen listede) önce sınıf isminin belirtilmesi zorunludur. Örneğin:

protocol A {

func foo()

}

protocol B {

func bar()

}

class Sample {

//…

}

class Mample : Sample, A, B {

func foo()

{

//…

}

func bar()

{

//…

}

//…

}

Bir protokol depolanmış property içeremez ancak hesaplanmış property içerebilir. Protokol içerisinde hesaplanmış property bildirimi yapılırken get ve set bölümlerinin gövdesi yazılmaz. Yalnızca get ve set anahtar sözcükleri bulundurulur. Örneğin:

protocol A {

func foo()

var count: Int {get set}

}

class Sample : A {

func foo()

{

//…

}

var count: Int {

get {

return 0

}

set {

//…

}

}

}

Protokoldeki hesaplanmış property’ler onu destekleyen türlerde depolanmış property biçiminde bildirilebilir. Örneğin:

protocol A {

func foo()

var count: Int {get set}

}

class Sample : A {

var count: Int = 0

func foo()

{

//…

}

}

Protokollerdeki property’ler read-only olabilir. Yani yalnızca get bölümüne sahip olabilir. Bu durumda o protokol desteklenirken o prperty read-only olarak ya da read/write olarak bildirilebilir. Örneğin:

protocol A {

func foo()

var count: Int { get }

}

class Sample : A {

var a: Int

init(_ a: Int)

{

self.a = a

}

func foo()

{

//…

}

var count: Int {

get {

return a

}

set {

a = newValue

}

}

}

Hesaplanmış property bildiriminde let anahtar sözcüğünün kullanılamadığını anımsayınız. Ancak read-only property içeren bir protokol depolanmış property ile desteklenirken let anahtar sözcüğü kullanılabilir. Örneğin:

protocol A {

func foo()

var count: Int { get }

}

class Sample : A {

let count: Int

init(_ a: Int)

{

self.count = a

}

func foo()

{

//…

}

}

Protokoller static metot içerebilir. Bu durumda o protokolü destekleyen türler o metodu static olarak bulundurumak zorundadır.

protocol A {

func foo()

var count: Int {get set}

static func bar() -> Int

}

class Sample : A {

func foo()

{

//…

}

var count: Int {

get {

return 0

}

set {

//…

}

}

static func bar() -> Int

{

return 0

}

}

Protokoller Doğuştan Çokbiçimli Mekanizmaya Dahildir

Protokoller türünden referanslar bildirilebilirler. Ancak nesneler yaratılamazlar. Örneğin:

protocol A {

func foo()

var count: Int {get set}

static func bar() -> Int

}

var a: A // geçerli

a = A() // error!

Bir sınıf türünden referans ya da yapı türünden değişken o sınıfın ya da yapının desteklediği protokol referanslarına doğrudan atanabilir. Örneğin Sample sınıfı A protokolünü destekliyor olsun:

var a: A

var s = Sample(100)

a = s // geçerli

Bu durum türemiş sınıftan taban sınıfa referans dönüştürmesine benzetilebilir. Tabii protokoller bu anlamda taban sınıf durumunda değillerdir.

Protokoller çokbiçimli mekanizmaya doğuştan dahil durumdadır. Yani bir protokol referansıyla bir protokol metodu ya da property’si kullanıldığında aslında o referansın dinamik türüne ilişkin sınıfın ya da yapının elemanı kullanılmış olur. Örneğin:

var a: A

var s = Sample()

a = s // geçerli

a.foo() // Sample.foo çağrılır

Örneğin:

import Foundation

protocol P {

func operate(a: Int, b: Int) -> Int

}

class A : P {

func operate(a: Int, b: Int) -> Int {

return a + b

}

}

class B : P {

func operate(a: Int, b: Int) -> Int {

return a * b

}

}

class C : P {

func operate(a: Int, b: Int) -> Int {

return a / b

}

}

func test(p: P, a: Int, b: Int)

{

let result = p.operate(a, b: b)

print(result)

}

let a = A()

let b = B()

let c = C()

test(a, a: 20, b: 10)

test(b, a: 20, b: 10)

test(c, a: 20, b: 10)

Protokollerde çokbiçimliliğin doğal bir durum olduğunu ve override gibi bir anahtar sözcüğün kullanılmadığına dikkat ediniz.

Taban sınıf bir protokolü destekliyorsa türemiş sınıfın da o protokolü desteklediği kabul edilir. Dolayısıyla biz türemiş sınıf türünden bir referansı taban sınıfın desteklediği protokol referansına doğrudan atayabilieiz. Örneğin:

protocol P {

func operate(a: Int, _ b: Int) -> Int

}

class A : P {

func operate(a: Int, _ b: Int) -> Int {

return a + b

}

}

class B : A {

//…

}

let p: P

let b = B()

p = b // geçerli

let result = p.operate(10, 20) // A.operate çağrılır

print(result)

Tabii türemiş sınıf taban sınıfın metodunu override edebilir. Bu durumda türemiş sınıftaki metot da çokbiçimli mekanizmaya dahil edilmiş olur. Örneğin:

import Foundation

protocol P {

func operate(a: Int, _ b: Int) -> Int

}

class A : P {

func operate(a: Int, _ b: Int) -> Int {

return a + b

}

}

class B : A {

override func operate(a: Int, _ b: Int) -> Int {

return a * b

}

}

let p: P

let b = B()

p = b // geçerli

let result = p.operate(10, 20) // B.operate çağrılır

print(result)

Türemiş sınıf bir protokolü destekliyor olsun. Bu protokolün elemanlarının bir kısmı taban sınıfta zaten bildirilmişse türemiş sınıf yalnızca geri kalan elemanları bildirebilir. Örneğin:

protocol P {

func foo()

func bar()

}

class A {

func foo()

{

//…

}

}

class B : A, P { // geçerli

func bar()

{

//…

}

}

Bir protokol türünden bir collection nesne söz konusu olabilir. Bu durumda o nesnenin içerisine biz o protokolü destekleyen herhangi bir sınıf, yapı ya da enum nesnelerini yerleştirebiliriz. Sonra hetorojen collection nesnemizi dolaşarak protokol metotlarını çağırabiliriz. Örneğin:

import Foundation

protocol P {

func foo()

}

class A : P {

func foo()

{

print(“A.foo”)

}

//…

}

class B : P {

func foo()

{

print(“B.foo”)

}

//…

}

struct C : P {

func foo()

{

print(“C.foo”)

}

//…

}

var ps: [P] = [A(), B(), A(), C(), B()]

for p in ps {

p.foo()

}

Protokollere Neden Gereksinim Duyulmaktadır?

Swift’te (Java ve C#’ta da böyle) protokollere gereksinim üç maddeyle açıklanabilir:

1) Protokoller Swift’te çoklu türetmenin olmamasının yarattığı boşluğu kısmen doldurmaktadır. Böylece bir sınıf ya da yapı farklı amaçlarla oluşturulmuş protokolleri destekleyerek farklı konulara ilişkin çokbiçimli davranışlar gösterebilmektedir.

2) Protokoller bir kontrat görevini de yerine getirirler. Yani bir sınıf, yapı ya da enum bir protokolü destekliyorsa kesinlikle o protokülün elemanlarını bulundurmak zorundadır.

3) Yapılar ve enum’lar türetmeye kapalıdır. Protokoller sayesinde onlar da çokbiçimli mekanizmaya dahil edilmişlerdir.

Protokollerde mutating Metotlar

Bir protokoldeki metot mutating anahtar sözcüğü ile bildirilirse o protokolü destekleyen yapılar ve enum’lar o metodu mutating olarak bildirmek zorundadır. Ancak o protokolü destekleyen sınıflar için mutating belirleyicinin bir anlamı yoktur. Örneğin:

import Foundation

protocol P {

mutating func foo()

}

struct S : P {

var a: Int

mutating func foo() // geçerli

{

//…

}

}

class C : P {

func foo() // geçerli

{

//…

}

}

Ayrıca protokollerdeki normal metotlar yapılarda ve enum’larda mutating olarak bildirilemez.

Protokoller init metotlarına sahip olabilir. Bu durumda o protokolü destekleyen sınıflarda ya da yapılarda o init metotlarının bulunuyor olması gerekir. Ancak böylesi bir durumda protokolü destekleyen sınıfta required anahtar sözcüğünün bulundurulması gerekir. Örneğin:

import Foundation

protocol P {

func foo()

func bar()

init(a: Int)

}

class A : P {

func foo()

{

//…

}

func bar()

{

//…

}

required init(a: Int) // required anahtar sözcüğü zorunlu

{

//…

}

//…

}

required belirleyicisi yapılarda ve enum’larda kullanılamadığı için init içeren protokolü bir yapı ya da enum destekliyorsa onların init bildirimlerinin önünde required belirleyicisi bulunmaz.

Protokollerde failable init metotları da bulunabilir. Bu init metotları o protokolü destekleyen sınıf ya da yapılarda failable olarak ya da normal olarak bildirilebilir. Örneğin:

protocol P {

init?()

func foo()

func bar()

}

class A : P {

required init?()

{

//…

}

func foo()

{

//…

}

func bar()

{

//…

}

//…

}

Ancak bu durumun tersi geçerli değildir. Yani protokoldeki init metodu normal ise biz onu destekleyen sınıf ya da failable olarak bildiremeyiz.

Eklentilerde Protokol Desteğinin Belirtilmesi

Bir sınıf ya da yapı için yazılan eklentiler o sınıf ya da yapıya protokol desteği verebilirler. Örneğin:

import Foundation

protocol P {

func foo()

}

class A {

init()

{

//…

}

//…

}

//…

extension A : P {

func foo()

{

//…

}

}

Tabii protokoldeki elemanlar zaten asıl sınıfta varsa bu durum yine anlamlıdır. Örneğin:

protocol P {

func foo()

}

class A {

init()

{

//…

}

func foo()

{

//…

}

//…

}

//…

extension A : P {

}

let p: P = A() // geçerli

Yalnızca Sınıfların Destekleyebildiği Protokoller

Swift’te (C# ve Java’da bu özellik yok) bir protokolün yalnızca sınıflar trafından desteklenmesi sağlanabilir. Bunun için protocol bildiriminde ‘:’ atomundan sonra class anahtar söcüğü yerleştirilir. Böyle protokolleri yapılar ve enum’lar destekleyemezler. Örneğin:

protocol P: class {

func foo()

}

class A : P { // geçerli

func foo()

{

print(“A.foo”)

}

//…

}

struct C : P { // error

func foo()

{

print(“C.foo”)

}

//…

}

Fakat bir protokolün yalnızca yapılar ve/veya enum’lar tarafından desteklenmesi gibi bir durum yoktur.

Protokollerin Birleştirilmesi (Protocol Composition)

Birden fazla protokol ile birleştirilmiş referanslar bildirilebilir. Bu durumda o referansa ancak o bildirimde belirtilen protokollerin hepsini destekleyen bir sınıf, yapı ya da enum değişkeni atanabilir. Bildirimde protokol birleştirme işlemi protocol<<protokol listesi>>. (Yani protocol anahtar sözcüğünden sonra açısal parantezler içerisinde protokol istesi belirtilmelidir.) ÖrneğiN:

import Foundation

protocol P1 {

func foo()

}

protocol P2 {

func bar()

}

func test(p: protocol<P1, P2>)

{

p.foo()

p.bar()

}

class Sample : P1, P2 {

func foo()

{

print(“Sample.foo”)

}

func bar()

{

print(“Sample.bar”)

}

}

let s = Sample()

test(s)

Şüphesiz açısal parantezler içerisindeki protokol listesinin sırasının bir önemi yoktur. Örneğin:

let p1: protocol<P1, P2> = Sample()

let p2: protocol<P2, P1> = p1 // geçerli

Protokollerde Türetme

Bir protokol başka bir protokolden türetilebilir. (Burada “türetme” terimi kullanılmaktadır. “Destekleme” terimi kullanılmamaktadır.) Bu durumda türemiş protokol sanki tabamn protokolün de elemanlarını içeriyormuş gibi bir etki söz konusu olur. Yani türemiş protokolü destekleyen bir sınıf, yapı ya da enum hem taban sınıfın hem de türemiş sınıfın elemanlarını bildirmek zorundadır. Örneğin:

protocol P1 {

func foo()

}

protocol P2 : P1 {

func bar()

}

class Sample : P2 {

func foo()

{

print(“Sample.foo”)

}

func bar()

{

print(“Sample.bar”)

}

}

Tıpkı C#’ta olduğu gibi sınıf, yapı ya da enum’un taban listesinde hem türemiş arayüzün hem de taban arayüzün ismi bulundurulabilir. Bunun hiçbir özel anlamı yoktur. Bazı programcılar okunabilirliği artırmak için bunu tercih edebilmektedir. Yani örneğin:

protocol P1 {

func foo()

}

protocol P2 : P1 {

func bar()

}

class Sample : P2 {

//…

}

ile:

protocol P1 {

func foo()

}

protocol P2 : P1 {

func bar()

}

class Sample : P2, P1 {

//…

}

tamamen eşdeğerdir.

Türemiş protokol referansı onun tüm taban protokol türlerine ilişkin referanslara doğrudan atanabilir. Örneğin:

let s = Sample()

let p2: P2 = s

let p1: P1 = p2 // geçerli, türemiş tabana atama

Protokollerde is ve as Operatörlerinin Kullanımı

Protokollerde de biz is operatörünü kullanabiliriz. Örneğin:

import Foundation

protocol P {

func foo()

}

class A : P {

func foo()

{

print(“A.foo”)

}

}

class B : A {

//…

}

class C : P {

func foo()

{

print(“C.foo”)

}

}

func test(p: P)

{

if p is A {

print(“p’nin dinamik türü A’yı içeriyor”)

}

if p is B {

print(“p’nin dinamik türü B’yi içeriyor”)

}

if p is C {

print(“p’nin dinamik türü C’yi içeriyor”)

}

print(“ — — — — — — — — — — — — — — — — — — — — -”)

}

let a = A()

let b = B()

let c = C()

test(a)

test(b)

test(c)

Protoller de as? ve as! operatörlerinin sol taraf operandı olarak kullanılabilir. Başka bir deyişle biz bir protokolü bir sınıf, yapı ya da enum türüne sşsğıya doğru dönüştürme işlemine (downcasting) sokabiliriz. Örneğin:

protocol P {

func foo()

}

class Sample : P {

var a: Int

init(a: Int)

{

self.a = a

}

func foo()

{

print(“A.foo”)

}

}

var p: P

var s: Sample

p = Sample(a: 10) // upcast

s = p as! Sample // downcast

print(s.a)

as! ve as? operatörlerinin sol tarafındaki protokol referansına ilişkin protokol türü sağ tarafındaki sınıf türü tarafından desteklenmiyor olsa bile derleme aşamasından başarı ile geçilir. Tabii kontrol yine programın çalışma zamanı sırasında yapılacaktır. Örneğin:

protocol P {

func foo()

}

class Sample {

//…

}

class Mample : P {

func foo()

{

print(“Mample.foo”)

}

//…

}

var p: P

var s: Sample

p = Mample()

s = p as! Sample // derleme aşamasından başarıyla geçilir, fakat exception oluşur

Pekiyi neden bu durumda denetim derleme aşamasında yapılamamaktadır? Çünkü belki de protokol referansının dinamik türü sağ taraftaki sınıftan türetilmiş bir tür türündendir.

Ancak bir protokol türünden referanss as! ya da as? operatörleriyle o protokol tarafından desteklenmeyen yapı ya da enum türüne dönüştürülmek istenirse denetim derleme aşamasında yapılır. Çünkü yapılar enum’lar türetmeye kapalıdır.

Bir sınıf türünden referans as! ya da as? operatörleriyle o sınıfın desteklemediği bir protokol türüne dönüştürülmek istenirse derleme aşamasından yine başarıyla geçilir. Denetim programın çalışma zamanı sırasında yapılır. Örneğin:

var s: Sample = Sample()

let p: P? = s as? P

Burada Sample sınıfı P protokülünü desteklemiyor olsa bile denetim derleme aşamasında yapılmaz. Programın çalışma zamanı sırasında yapılır. Tabi yine yukarıdakiyle aynı gerekçelerle bir yapı ya da enum türü as! ya da as? operatörleriyle o yapı ya da enum türünün desteklemediği bir protokol türüne dönüştürülmeye çalışılırsa denetim derleme aşamasında yapılır.

Swift’te yine bir protol türünden referans her zaman aralarında türetme ilişkisi olmasa bile başka bir protokol türüne as! ya da as? operatörleriyle dönüştürülmek istenebilir. Bu durumda denetim derleme aşamasında yapılmaz. Programın çalışma zamanı sırasında yapılır. Örneğin:

import Foundation

protocol P1 {

func foo()

}

protocol P2 {

func foo()

}

class Sample : P1 {

func foo()

{

//…

}

//…

}

let p1: P1 = Sample()

var p2: P2

p2 = p1 as! P2 // derleme aşamasındna her zaman başarıyla geçilşir, denetim çalışma zamanında yapılır

Programın çalışma zamanı sırasında dönüştürülmek istenen protokol referansının (örneğimizdeki p1) dinamik türünün dönüştürülmek istenen protokolü (örneğimizdeki P2) destekleyip desteklemediğine bakılır.

Protokol Eklentileri (Protocol Extensions)

Bir protokol için eklenti oluşturulabilir. Böylelikle protokole yeni elemanlar eklenebilir. Ancak eklentide eklenen elemamların gövde içermesi zorunludur. Örneğin:

import Foundation

protocol P {

func foo()

}

//…

extension P {

func bar()

{

print(“P.bar”)

}

}

class Sample : P {

func foo()

{

//…

}

//…

}

let s = Sample()

s.foo() // geçerli

s.bar() // geçerli

let p: P = s

p.foo() // geçerl,

p.bar() // geçerli

Anahtar Notlar: Swift’in orijinal dokümanlarında açıkça belirtilmemiş olsa da Swift derleyicisi ilgili sınıf ya da yapıda yeniden protokoldeki elemanı bildirdiğinde sorun çıkartmamaktadır. Bu durumda aşağıdan yani sınıf ya da yapı değişkeni yoluyla bu eleman kullanıldığında sınıf ya da yapının içerisinde bildirilmiş elemanın kullanıldığı varsayılmaktadır. Ancak protokol yoluyla kullanımda eklentide bildirilmiş olan elemanın kullanıldığı varsayılmaktadır.

--

--