Objective-C 【protocol-協議 的瞭解使用】
阿新 • • 發佈:2019-02-03
———————————————————————————————————————————
protocol(協議)
(1)引文:OC中的protocol和Java中的介面(interface)比較類似,Java中的介面(interface)就是一堆方法的宣告(沒有實現),而OC中的interface是一個類的標頭檔案的宣告,並不是真正意義上的介面,在OC中,介面是一個叫做 協議 的 protocol 來實現的。
和Java不同,protocol 可以宣告一些必須實現的方法和一些選擇實現的方法。(Java中如果要實現介面,就要必須實現介面中的所有方法,否則報錯。 而protocol中宣告的方法可以實現也可以不實現,這裡其實預設也是全部都要實現的,只不過OC是弱語法語言,如果沒有實現protocol中的方法,只會發出警告,而不會報錯)
(2)OC中的協議(protocol):
①Protocol:就一個用途,用來宣告一大堆的方法。(但是不能宣告成員變數,也不能實現方法)
②只要某個類遵守了某個協議,就擁有了這個協議中的所有的 方法宣告(實現還得自己去實現。如果是繼承,則繼承了父類的宣告和實現,這和protocol是不同的)
③只要父類遵守了某個協議,那麼子類也得遵守
④Protocol宣告的方法可以讓任何類去實現,Protocol就是協議
⑤OC不能繼承多個類(遵守單繼承原則),但是能夠遵守多個協議。繼承(:),遵守協議(< >)
⑥基協議: <NSObject> 是基協議,是最根本最基本的協議,其中聲明瞭很多最基本的方法。
⑦協議可以遵守協議,一個協議遵守了另一個協議,就可以擁有另一份協議中的方法宣告。
(3)一般定義協議是在.h檔案中,而誰採用這個協議就在誰的.m檔案中實現方法。
①定義協議
@protocol 協議名稱 <NSObject>
//方法宣告列表
@end
注意:協議預設的要採納NSObject的協議。
②採用協議
★★★採用協議的時候要引入協議的標頭檔案,然後再讓 類/協議 去遵守協議
★某個類遵守 某個協議 或者 某些協議,一個類可以遵守其他多個協議:
@interface 類名:父類 <協議名稱1,協議名稱2,協議名稱3>
@end
★某個協議遵守 某個協議 或者 某些協議,一個協議可以遵守其他多個協議:(協議之間的繼承關係)
@protocol 協議名稱 <其他協議名稱1,其他協議名稱2,其他協議名稱3>
@end
③實現協議中的方法
在類的.m檔案中實現
接下來我們用一段程式碼來描述上述三個操作(①②③):
★main.m★
#import <Foundation/Foundation.h>
#import "Car.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
Car *car1=[[Car alloc]init];
[car1 run];
[car1 train];
}
return 0;
}
★runProtocol.h★
#import <Foundation/Foundation.h>
@protocol runProtocol <NSObject>
//runProtocol協議遵守NSObject(基協議)
-(void)run;
-(void)jump;
-(void)jump2;
@end
★toolProtocol.h★
#import <Foundation/Foundation.h>
#import "runProtocol.h" //我們引入之間建立的協議runProtocol的標頭檔案
//協議遵守其他協議
@protocol toolProtocol <NSObject,runProtocol> //然後讓toolProtocol協議去遵守NSObject和runProtocol兩個協議(這裡注意,NSObject是每個協議建立的時候都要遵守的)
-(void)plane;
-(void)train;
@end
★Car.h★
#import <Foundation/Foundation.h>
#import "toolProtocol.h"
//#import "runProtocol.h" //因為我們讓toolProtocol協議去遵守runProtocol協議,所以只匯入toolProtocol協議的標頭檔案即可
//我們要引入我們遵守的協議的標頭檔案
//類遵守協議
@interface Car : NSObject <toolProtocol> //引入標頭檔案後才能第二步遵守協議。這裡也是隻遵守toolProtocol協議就行了,這相當於讓Car類同時遵守了toolProtocol和runProtocol兩個協議。
@end
★Car.m★
#import "Car.h"
@implementation Car
//第三步才是實現協議中的方法
-(void)run
{
NSLog(@"run!");
}
-(void)jump
{
NSLog(@"jump!");
}
-(void)jump2
{
NSLog(@"jump2!");
}
-(void)plane
{
NSLog(@"plane!");
}
-(void)train
{
NSLog(@"train!");
}
@end
(4)如果父類遵守一個協議,那麼他的子類也相應遵守這個協議。
(5)NSObject是一個基類,同時也有NSObject這個基協議。(這個在上面的程式碼中已經提到)
———————————————————————————————————————————
protocol中的@required和@optional
@required和@optional 是協議方法宣告中的兩個至關重要的關鍵字,他們的作用主要控制方法是否必須要實現!
@required —— 必須實現(編譯器預設是@required,若不實現,會警告)
@option —— 可以選擇實現
用途在於程式設計師之間的交流。OC是弱語法,一旦沒有寫方法實現,能提醒別人知道。警告一下但是不報錯,別人用你的程式碼的時候可以很好的提示一下。
我們建立了一個foodProtocol的協議,裡面有@required屬性的方法和@optional屬性的方法。
警告顯示eat方法在foodProtocol中聲明瞭但是沒有實現,但是卻沒有提示bark方法,顯然原因大家已經知道了。
而這就是@required 和 @optional 關鍵字的用法。
———————————————————————————————————————————
protocol型別限制
(1)使用id儲存物件時,物件的型別限制
格式:id <協議名稱> 變數名;
如: id <Myprotocol> obj1;
(2)物件建立時型別限制
格式:類名 <協議名稱> *變數名
如:Person <houseHold> *p= [ [ Student alloc ] init ];
(3)物件關聯關係下,物件的型別限制
如:
@property (nonatomic,strong) id<girlFriend> girlfriend;
//建立一個girl的成員變數,表示創建出來的Person的例項物件應該擁有一個女友,而且女友需要遵守girlFriend的協議
程式碼:
★girlFriend★
#import <Foundation/Foundation.h>
@protocol girlFriend <NSObject>
//girlFriend必須遵守的(會做飯洗衣)
@required
-(void)cook;
-(void)washClothes;
//允許girlFriend不遵守的(有國企工作的優先)
@optional
-(void)goodJob;
@end
★Person.h★
#import <Foundation/Foundation.h>
#import "girlFriend.h"
@interface Person : NSObject
//建立一個girl的成員變數,表示創建出來的Person的例項物件應該擁有一個女友,而且女友需要遵守girlFriend的協議
//增加限制id<girlFriend>
//表示,id儲存的物件必須實現了girlFriend協議
//找的女朋友,必須會洗衣做飯
@property (nonatomic,strong) id<girlFriend> girlfriend;
@end
★Person.m★
#import "Person.h"
@implementation Person
@end
★Girl.h★
#import <Foundation/Foundation.h>
#import "girlFriend.h"
@interface Girl : NSObject <girlFriend> //這裡寫上Girl類遵守girlFriend協議
@end
★Girl.m★
#import "Girl.h"
@implementation Girl
-(void)cook
{
NSLog(@"cook dinner~");
}
-(void)washClothes
{
NSLog(@"wash clothes~");
}
@end
★mian.m★
#import <Foundation/Foundation.h>
#import "Person.h"
#import "Girl.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
Girl *girl1=[Girl new];
Person *p1=[Person new];
Person *p2=[Person new];
p1.girlfriend=girl1; //這裡不會報錯,因為girl1是Girl型別的變數,所以遵守girlFriend協議,可以賦值
[p1.girlfriend cook];
[p1.girlfriend washClothes];
// [p1.girlfriend cook]; //隨便用一個p2來賦給p1,做p1的女朋友是不行的
}
return 0;
}
———————————————————————————————————————————
protocol的代理模式引入
MVC(三層框架)
m - model
v - view
c - controller
★代理模式概念:
傳入的物件,代替當前類完成了某個功能,稱為代理模式
代理模式應用場合:
①當物件A發生了一些行為,想告知物件B(讓物件B成為物件A的代理物件)
②物件B想監聽物件A的一些行為(讓物件B成為物件A的代理物件)
③當物件A無法處理某些行為的時候(讓物件B成為物件A的代理物件)
★我們可以看出,在上面的三個場合中,B需要滿足協議要求的,才能幫助/代替A幹某些事情。這就建立了一個代理模式應用場合。當然滿足代理模式應用的場合還有許多。
下面是一個這樣的程式:baby有吃東西和睡覺的方法,如果baby睡覺或者是吃飯都要去讓一個人來幫助他完成(在吃東西和睡覺方法中呼叫另外一個物件的方法),而保姆就是那個具備照顧baby要求的人選(遵守了協議)。現在我們要求一個老師去照顧baby,幫助baby去完成吃飯睡覺的工作,但老師是不遵守協議的,然後我們要讓老師也遵循這個協議去照顧baby。
形容的實在太囉嗦了,原諒我的口才不是太好。
問題的關鍵先建立模型,將方法寫好之後再考慮協議的問題。一步一步的新增上述要求。
程式碼:
// babysitProtocol.h
#import <Foundation/Foundation.h>
@class Baby;
@protocol babysitProtocol <NSObject>
-(void)feedBaby:(Baby *)baby;
-(void)hongBabyToSleep:(Baby *)baby;
@end
// Baby.h
@interface Baby : NSObject
@property (nonatomic) id <babysitProtocol> bm;
@property (nonatomic) float weight;
@property (nonatomic) float height;
-(void)wantEat;
-(void)wantSleep;
@end
// Baby.m
#import "Baby.h"
#import "Baomu.h"
@implementation Baby
-(void)wantEat
{
NSLog(@"baby want eat!");
[self.bm feedBaby:self];
}
-(void)wantSleep
{
NSLog(@"baby want sleep!");
[self.bm hongBabyToSleep:self];
}
@end
// Baomu.h
#import <Foundation/Foundation.h>
#import "babysitProtocol.h"
@class Baby;
@interface Baomu : NSObject <babysitProtocol>
-(void)feedBaby:(Baby *)baby;
-(void)hongBabyToSleep:(Baby *)baby;
@end
// Baomu.m
#import "Baomu.h"
#import "Baby.h"
@implementation Baomu
-(void)feedBaby:(Baby *)baby
{
NSLog(@"Baomu feed baby!");
baby.weight+=1;
}
-(void)hongBabyToSleep:(Baby *)baby
{
NSLog(@"Baomu baby sleep!");
baby.height+=0.5;
}
@end
// Teacher.h
#import <Foundation/Foundation.h>
#import "babysitProtocol.h"
@interface Teacher : NSObject <babysitProtocol>
@end
// Teacher.m
#import "Teacher.h"
#import "Baby.h"
@implementation Teacher
-(void)feedBaby:(Baby *)baby
{
NSLog(@"Teacher feed baby!");
baby.weight+=1;
}
-(void)hongBabyToSleep:(Baby *)baby
{
NSLog(@"Teacher baby sleep!");
baby.height+=0.5;
}
@end
// main.m
//一般而言什麼 類/協議 遵守另外的協議,後面的協議尖括號是緊跟著 類/協議 去寫的,不要將協議的位置寫錯
#import <Foundation/Foundation.h>
#import "Baby.h"
#import "Baomu.h"
#import "Teacher.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
Baby *baby1=[Baby new];
Baomu *baomu1=[Baomu new];
baby1.bm=baomu1;
[baby1 wantEat];
//以上四句話是非常合理的,因為baomu1是Baomu的例項物件,baomu1遵守babysitProtocol協議,所以說baby1可以擁有他,並且呼叫。
//但是如果寫下面兩句顯然就有錯了,因為我們並沒有讓Teacher類去遵守babysitProtocol這個協議。
// Teacher *teacher1=[Teacher new];
// baby1.bm=teacher1;
//那麼我們應該怎麼改進呢?
// 顯然,我們應該讓Teacher類也遵守babysitProtocol協議,然後將Baby中的bm屬性設定為id型別,這樣Teacher也能feedBaby和hongBabyToSleep了~
// 具體程式碼在各個檔案中已經改變~
Baby *baby2=[Baby new];
Teacher *teacher2=[Teacher new];
baby2.bm=teacher2;
[baby2 wantSleep];
}
return 0;
}
———————————————————————————————————————————
protocol(協議)
(1)引文:OC中的protocol和Java中的介面(interface)比較類似,Java中的介面(interface)就是一堆方法的宣告(沒有實現),而OC中的interface是一個類的標頭檔案的宣告,並不是真正意義上的介面,在OC中,介面是一個叫做 協議 的 protocol 來實現的。
和Java不同,protocol 可以宣告一些必須實現的方法和一些選擇實現的方法。(Java中如果要實現介面,就要必須實現介面中的所有方法,否則報錯。 而protocol中宣告的方法可以實現也可以不實現,這裡其實預設也是全部都要實現的,只不過OC是弱語法語言,如果沒有實現protocol中的方法,只會發出警告,而不會報錯)
(2)OC中的協議(protocol):
①Protocol:就一個用途,用來宣告一大堆的方法。(但是不能宣告成員變數,也不能實現方法)
②只要某個類遵守了某個協議,就擁有了這個協議中的所有的 方法宣告(實現還得自己去實現。如果是繼承,則繼承了父類的宣告和實現,這和protocol是不同的)
③只要父類遵守了某個協議,那麼子類也得遵守
④Protocol宣告的方法可以讓任何類去實現,Protocol就是協議
⑤OC不能繼承多個類(遵守單繼承原則),但是能夠遵守多個協議。繼承(:),遵守協議(< >)
⑥基協議: <NSObject> 是基協議,是最根本最基本的協議,其中聲明瞭很多最基本的方法。
⑦協議可以遵守協議,一個協議遵守了另一個協議,就可以擁有另一份協議中的方法宣告。
(3)一般定義協議是在.h檔案中,而誰採用這個協議就在誰的.m檔案中實現方法。
①定義協議
@protocol 協議名稱 <NSObject>
//方法宣告列表
@end
注意:協議預設的要採納NSObject的協議。
②採用協議
★★★採用協議的時候要引入協議的標頭檔案,然後再讓 類/協議 去遵守協議
★某個類遵守 某個協議 或者 某些協議,一個類可以遵守其他多個協議:
@interface 類名:父類 <協議名稱1,協議名稱2,協議名稱3>
@end
★某個協議遵守 某個協議 或者 某些協議,一個協議可以遵守其他多個協議:(協議之間的繼承關係)
@protocol 協議名稱 <其他協議名稱1,其他協議名稱2,其他協議名稱3>
@end
③實現協議中的方法
在類的.m檔案中實現
接下來我們用一段程式碼來描述上述三個操作(①②③):
★main.m★
#import <Foundation/Foundation.h>
#import "Car.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
Car *car1=[[Car alloc]init];
[car1 run];
[car1 train];
}
return 0;
}
★runProtocol.h★
#import <Foundation/Foundation.h>
@protocol runProtocol <NSObject>
//runProtocol協議遵守NSObject(基協議)
-(void)run;
-(void)jump;
-(void)jump2;
@end
★toolProtocol.h★
#import <Foundation/Foundation.h>
#import "runProtocol.h" //我們引入之間建立的協議runProtocol的標頭檔案
//協議遵守其他協議
@protocol toolProtocol <NSObject,runProtocol> //然後讓toolProtocol協議去遵守NSObject和runProtocol兩個協議(這裡注意,NSObject是每個協議建立的時候都要遵守的)
-(void)plane;
-(void)train;
@end
★Car.h★
#import <Foundation/Foundation.h>
#import "toolProtocol.h"
//#import "runProtocol.h" //因為我們讓toolProtocol協議去遵守runProtocol協議,所以只匯入toolProtocol協議的標頭檔案即可
//我們要引入我們遵守的協議的標頭檔案
//類遵守協議
@interface Car : NSObject <toolProtocol> //引入標頭檔案後才能第二步遵守協議。這裡也是隻遵守toolProtocol協議就行了,這相當於讓Car類同時遵守了toolProtocol和runProtocol兩個協議。
@end
★Car.m★
#import "Car.h"
@implementation Car
//第三步才是實現協議中的方法
-(void)run
{
NSLog(@"run!");
}
-(void)jump
{
NSLog(@"jump!");
}
-(void)jump2
{
NSLog(@"jump2!");
}
-(void)plane
{
NSLog(@"plane!");
}
-(void)train
{
NSLog(@"train!");
}
@end
(4)如果父類遵守一個協議,那麼他的子類也相應遵守這個協議。
(5)NSObject是一個基類,同時也有NSObject這個基協議。(這個在上面的程式碼中已經提到)
★而且,檢視原始碼得知:基類也是遵守基協議的。
———————————————————————————————————————————
protocol中的@required和@optional
@required和@optional 是協議方法宣告中的兩個至關重要的關鍵字,他們的作用主要控制方法是否必須要實現!
@required —— 必須實現(編譯器預設是@required,若不實現,會警告)
@option —— 可以選擇實現
用途在於程式設計師之間的交流。OC是弱語法,一旦沒有寫方法實現,能提醒別人知道。警告一下但是不報錯,別人用你的程式碼的時候可以很好的提示一下。
這段知識比較簡單,我用截圖給大家說明一下:
我們建立了一個foodProtocol的協議,裡面有@required屬性的方法和@optional屬性的方法。
我們讓Dog類遵守foodProtocol協議。
我們選擇實現其中一個@required型別的方法(drink),而其他的兩個方法不實現,然後我們發現了一個警告。
警告顯示eat方法在foodProtocol中聲明瞭但是沒有實現,但是卻沒有提示bark方法,顯然原因大家已經知道了。
而這就是@required 和 @optional 關鍵字的用法。
———————————————————————————————————————————
protocol型別限制
(1)使用id儲存物件時,物件的型別限制
格式:id <協議名稱> 變數名;
如: id <Myprotocol> obj1;
(2)物件建立時型別限制
格式:類名 <協議名稱> *變數名
如:Person <houseHold> *p= [ [ Student alloc ] init ];
(3)物件關聯關係下,物件的型別限制
如:
@property (nonatomic,strong) id<girlFriend> girlfriend;
//建立一個girl的成員變數,表示創建出來的Person的例項物件應該擁有一個女友,而且女友需要遵守girlFriend的協議
程式碼:
★girlFriend★
#import <Foundation/Foundation.h>
@protocol girlFriend <NSObject>
//girlFriend必須遵守的(會做飯洗衣)
@required
-(void)cook;
-(void)washClothes;
//允許girlFriend不遵守的(有國企工作的優先)
@optional
-(void)goodJob;
@end
★Person.h★
#import <Foundation/Foundation.h>
#import "girlFriend.h"
@interface Person : NSObject
//建立一個girl的成員變數,表示創建出來的Person的例項物件應該擁有一個女友,而且女友需要遵守girlFriend的協議
//增加限制id<girlFriend>
//表示,id儲存的物件必須實現了girlFriend協議
//找的女朋友,必須會洗衣做飯
@property (nonatomic,strong) id<girlFriend> girlfriend;
@end
★Person.m★
#import "Person.h"
@implementation Person
@end
★Girl.h★
#import <Foundation/Foundation.h>
#import "girlFriend.h"
@interface Girl : NSObject <girlFriend> //這裡寫上Girl類遵守girlFriend協議
@end
★Girl.m★
#import "Girl.h"
@implementation Girl
-(void)cook
{
NSLog(@"cook dinner~");
}
-(void)washClothes
{
NSLog(@"wash clothes~");
}
@end
★mian.m★
#import <Foundation/Foundation.h>
#import "Person.h"
#import "Girl.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
Girl *girl1=[Girl new];
Person *p1=[Person new];
Person *p2=[Person new];
p1.girlfriend=girl1; //這裡不會報錯,因為girl1是Girl型別的變數,所以遵守girlFriend協議,可以賦值
[p1.girlfriend cook];
[p1.girlfriend washClothes];
p1.girlfriend=p2; //這裡會報錯,因為p2是Person型別的,而Person並不遵守這個協議,所以無法賦值
// [p1.girlfriend cook]; //隨便用一個p2來賦給p1,做p1的女朋友是不行的
}
return 0;
}
———————————————————————————————————————————
protocol的代理模式引入
MVC(三層框架)
m - model
v - view
c - controller
★代理模式概念:
傳入的物件,代替當前類完成了某個功能,稱為代理模式
代理模式應用場合:
①當物件A發生了一些行為,想告知物件B(讓物件B成為物件A的代理物件)
②物件B想監聽物件A的一些行為(讓物件B成為物件A的代理物件)
③當物件A無法處理某些行為的時候(讓物件B成為物件A的代理物件)
★我們可以看出,在上面的三個場合中,B需要滿足協議要求的,才能幫助/代替A幹某些事情。這就建立了一個代理模式應用場合。當然滿足代理模式應用的場合還有許多。
下面是一個這樣的程式:baby有吃東西和睡覺的方法,如果baby睡覺或者是吃飯都要去讓一個人來幫助他完成(在吃東西和睡覺方法中呼叫另外一個物件的方法),而保姆就是那個具備照顧baby要求的人選(遵守了協議)。現在我們要求一個老師去照顧baby,幫助baby去完成吃飯睡覺的工作,但老師是不遵守協議的,然後我們要讓老師也遵循這個協議去照顧baby。
形容的實在太囉嗦了,原諒我的口才不是太好。
問題的關鍵先建立模型,將方法寫好之後再考慮協議的問題。一步一步的新增上述要求。
程式碼:
// babysitProtocol.h
#import <Foundation/Foundation.h>
@class Baby;
@protocol babysitProtocol <NSObject>
-(void)feedBaby:(Baby *)baby;
-(void)hongBabyToSleep:(Baby *)baby;
@end
// Baby.h
@interface Baby : NSObject
@property (nonatomic) id <babysitProtocol> bm;
@property (nonatomic) float weight;
@property (nonatomic) float height;
-(void)wantEat;
-(void)wantSleep;
@end
// Baby.m
#import "Baby.h"
#import "Baomu.h"
@implementation Baby
-(void)wantEat
{
NSLog(@"baby want eat!");
[self.bm feedBaby:self];
}
-(void)wantSleep
{
NSLog(@"baby want sleep!");
[self.bm hongBabyToSleep:self];
}
@end
// Baomu.h
#import <Foundation/Foundation.h>
#import "babysitProtocol.h"
@class Baby;
@interface Baomu : NSObject <babysitProtocol>
-(void)feedBaby:(Baby *)baby;
-(void)hongBabyToSleep:(Baby *)baby;
@end
// Baomu.m
#import "Baomu.h"
#import "Baby.h"
@implementation Baomu
-(void)feedBaby:(Baby *)baby
{
NSLog(@"Baomu feed baby!");
baby.weight+=1;
}
-(void)hongBabyToSleep:(Baby *)baby
{
NSLog(@"Baomu baby sleep!");
baby.height+=0.5;
}
@end
// Teacher.h
#import <Foundation/Foundation.h>
#import "babysitProtocol.h"
@interface Teacher : NSObject <babysitProtocol>
@end
// Teacher.m
#import "Teacher.h"
#import "Baby.h"
@implementation Teacher
-(void)feedBaby:(Baby *)baby
{
NSLog(@"Teacher feed baby!");
baby.weight+=1;
}
-(void)hongBabyToSleep:(Baby *)baby
{
NSLog(@"Teacher baby sleep!");
baby.height+=0.5;
}
@end
// main.m
//一般而言什麼 類/協議 遵守另外的協議,後面的協議尖括號是緊跟著 類/協議 去寫的,不要將協議的位置寫錯
#import <Foundation/Foundation.h>
#import "Baby.h"
#import "Baomu.h"
#import "Teacher.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
Baby *baby1=[Baby new];
Baomu *baomu1=[Baomu new];
baby1.bm=baomu1;
[baby1 wantEat];
//以上四句話是非常合理的,因為baomu1是Baomu的例項物件,baomu1遵守babysitProtocol協議,所以說baby1可以擁有他,並且呼叫。
//但是如果寫下面兩句顯然就有錯了,因為我們並沒有讓Teacher類去遵守babysitProtocol這個協議。
// Teacher *teacher1=[Teacher new];
// baby1.bm=teacher1;
//那麼我們應該怎麼改進呢?
// 顯然,我們應該讓Teacher類也遵守babysitProtocol協議,然後將Baby中的bm屬性設定為id型別,這樣Teacher也能feedBaby和hongBabyToSleep了~
// 具體程式碼在各個檔案中已經改變~
Baby *baby2=[Baby new];
Teacher *teacher2=[Teacher new];
baby2.bm=teacher2;
[baby2 wantSleep];
}
return 0;
}
———————————————————————————————————————————