OC學習之旅------第九天

沒有撤退可言~發表於2021-01-02

Block基礎概念

在這裡插入圖片描述


//首先回憶一下什麼是函式指標 
#import <Foundat ion/ Foundation. h>

void printRose( )
{
	printf(" {@} \n");
	printf("  |  \n");
	printf(" \\|/ \n");
	printf("  |");
}
int main(int argc,const char * argv[]) 
{
	printRose() ;
	void (*roseP) ();
	roseP = printRose;
	roseP();
	return 0;
}


//
	// void代表指向的函式沒有返回值
	// ()代表指向的函式沒有形參
	// (*roseP )代表roseP是一個指向函式的指標
	void (*roseP) ();
	roseP = printRose;
	roseP();
	//定義一個block變數,
	// block和函式一樣,可以沒有返回值,也沒有形參
	//也可以沒有返回值有形參
	//也可以有返回值沒有形參
	//也可以有返回值有形參
	//所以,在定義一個block變數的時候,也需要告訴該變數將來儲存的程式碼有沒有返回值和形
	// void代表b lock將來儲存的程式碼沒有返回值
	// ()代表block將 來儲存的程式碼沒有形參
	// (^roseBlock) 代表reseBlock是一個block變數,可以用於儲存一-段block程式碼
	//下面的寫法就是先定義再初始化
	void (^roseBlock) ();
	roseBlock = ^{
	//roseBlock = ^(){    一般是這個樣子寫,沒有引數可以不寫(),有引數就給()裡面寫引數
		printf(" {@} \n");
		printf("  |  \n");
		printf(" \\|/ \n");
		printf("  |");
	};
	//到這裡就相當於定義了一個block函式,他和函式指標很想,只不過他把函式的實現全部都寫在了函式內部
	//要想執行block儲存的程式碼,必須呼叫block才會執行
	roseBlock( ) ;  //這句話就是呼叫block函式


//定義一個變數有兩種方式一種是先定義在初始化,一種是在定義的時候直接初始化
//1.
//int a;
//a = 10

//2.
//int a = 10;
//所以說上面的程式碼還可以這樣寫
void (^roseBlock) () = ^(){  //如果沒有引數這個()可以省略
//還有一種寫法就是假如block代表的這個函式有引數,和返回值
//void (^roseBlock) (int) = ^int(int){ 一般^後面的這個int也就是返回值一般都是省略
		printf(" {@} \n");
		printf("  |  \n");
		printf(" \\|/ \n");
		printf("  |");
	};

在這裡插入圖片描述
在這裡插入圖片描述

block和typedef

//首先先回憶一下typedef怎麼使用
int sum(int value1, int value2)
{
	return value1 + value2;
}
int minus(int value1, int value2)
{
	return value1 - value2;
}
typedef int (*calculte)(int, int);
int main(int argc,const char * argv[]) 
{
	//呼叫加法
	calculte sumP = sum;
	NSLog (@"sum = %i", sumP(2010));
I
	//呼叫減法
	calculte minusP = minus ;
	NSLog (@"minus = %i", minusP(20, 10));
	return 0; 
}


//接著使用block和typedef的結合試試
//注意:利用typedef給block起別名, 和指向函式的指標一樣,block變數的名稱就是別名
typedef int (^calculteBlock)( int,int);
 
calculteBlock sumBlock = ^(int value1, int value2){
	return value1 + value2;
};
NSLog(@"sum = %i", sumBlock(20, 10));

block的應用場景,比如現在有一系列的操作,這一系列操作裡面重複的步驟很多,比如說我們開啟一個檔案往檔案裡面寫東西,是不是我們開啟檔案和關閉檔案的一系列操作都是一樣的,此時我們就可以,只有開啟檔案之後在檔案內部進行的操作不一樣,我們可以把在檔案內部的操作封裝成一個block函式,然後再有一個函式前半部分是開啟後半部分是關閉檔案,此時我們可以把我們封裝的block方法插入到開啟和關閉中間,這個時候每次呼叫王往檔案裡面寫東西這個操作的時候就可以,把檔案裡面寫的內容當作引數傳遞給block,block又包含在開啟關閉檔案之間,所以就會減少程式碼的重複

//當發現程式碼的前面和後面都是一 樣的時候,這個時候就可以使用block
void goToWork(void (^workBlock)()) //引數代表是一個block型別沒有引數沒有返回值
{
	NSLog(@"起床") ;
	NSLog(@"穿衣服");
	NSLog(@"洗漱");
	NSLog(@"喝早茶");
	NSLog(@"駕車去上班");

	//不一樣
	workBlock();
	
	NSLog(@"收拾東西");
	NSLog(@"駕車回家");
	NSLog(@"吃晚飯");
	NSLog(@"洗澡");
	NSLog(@"睡覺");
}

//呼叫
void goToWorkInDay1( )
{
	goToWork(^{
		NSLog(@"認識新同事");
	});
}

block注意事項

#import <Foundation/ Foundation. h>
int main(int argc, const char * argv[]) 
{
	// 1. block中可以訪問外面的變數
	/*
	int a=10;
	void (^myBlock)() = ^{
		NSLog(@"a = %i", a);
	};
	myBlock();
	*/

	// 2. block中可以定義和外界同名的變數,並且如果在block中定義了和外界同名的變數,在block中訪問的是block中的變數
	/*
	int a=10;
	void (^myBlock)() = ^{
		int a = 20;
		NSLog(@"a = %i", a);
	};
	myBlock();
*/

在這裡插入圖片描述
在這裡插入圖片描述
在這裡插入圖片描述
在這裡插入圖片描述

現在來總結一下,為啥加了_block在內部的修改就可以影響到外部,而不加就無法修改麼?

	可以講OC程式碼在命令列通過cc --help檢視幫助指令,並且通過一個引數轉換成C++程式碼,通過對C++程式碼
分析後可以看出,block方法其實底層是處理成函式,然後按照函式的方式去傳遞引數的,在不使用_block的時
候按照的將外部的a是按照值傳遞的方式來傳參的,所以在block函式內部對a進行修改並不會影響到外部的a,但
是在變數前加上_block之後就是按照指標傳遞地址的方式,所以在內部是可以修改外部的a的。

上面的地址為啥列印結果不同,我也不知道

在這裡插入圖片描述
在這裡插入圖片描述

還有一個就是在MRC模式下的注意事項:
在這裡插入圖片描述

協議的基本概念:

Protocal基本概念:
注意協議他也就是一種介面
在這裡插入圖片描述
Protocal 語法格式:
在這裡插入圖片描述
協議就是由方法的宣告組成的:
首先建立一個Objective-c File
在這裡插入圖片描述
接著檔案型別選擇Protocal。接著我們就使用了一個Xcode模板來建立了一個協議模板,協議只有.h檔案
在這裡插入圖片描述
協議是什麼?協議有什麼用?

1.協議只有.h檔案,裡面只有函式的宣告,沒有實現,為什麼沒有實現呢?誰遵守我,誰去實現,
也就是可以保證實現方法的多樣性,但是又可以保證介面的一致性

2.所有的類都可以遵守,也就是把我的協議當成模板,實現我模板裡面的方法
//Sportprotocal.h  //協議
#import <Foundation/ Foundation. h>
@protocol SportProtocol <NS0bject>
//方法宣告列表
-( void) playFootball;
- (void) playBasketball;
- (void) playBaseball;
@end
//Person.h
#import <Foundat ion/ Foundation. h>
#import "SportProtocol. h"
@interface Person : NS0bject <SportProtocol>
@end
//Person.m
#import "Person. h"  //注意包含標頭檔案
@implementation Person
- (void)playFootball
{
	NSLog(@"s",__func__) ;
}

- (void)playBasketball
{
	NSLog(@"%s",__func__);
}
- (void) playBaseball
{
	NSLog (@"%s",__func__ ) ;
}
@end
//main.m
#import <Foundat ion/ Foundation. h>
#import "Person. h"
#import "Student. h"
int main(int argc, const char * argv[]) 
{
	//下面就可以直接進行呼叫
	Person *p = [Person new] ;
	[p playFootball] ;
	[p playBasketball];
	[p playBaseball]; 
	return 0;
}

協議和繼承的區別

在這裡插入圖片描述

協議比繼承的使用場景更廣一點

協議注意事項

協議和分類差不多就是在不修改原有類的基礎上給這個類增加一些功能
不同的是,協議只有宣告沒有實現,而分類是既有宣告也有實現
我們在學習分類的時候,分類只能擴充方法不能擴充屬性,協議也一樣

1.遵循協議的時候需要新增協議的標頭檔案

2.只有父類遵循了某個協議,子類繼承下來之後也會遵循這個協議

3.在OC中一個類可以遵守一個或者多個協議,順便提一嘴,OC中的類只有能一個父類,這個和C++不一樣

4.OC中的協議又可以遵守其它協議,只要-一個協議遵守了其它協議,那麼這個協議中就會自動包含其它協議的宣告

5.協議只能宣告方法不能宣告屬性

6.我們新建立的類遵守NSObject協議,我們新建立的協議也要遵守NSObject協議

在這裡插入圖片描述
在這裡插入圖片描述
NSObject這個類遵守了NSObject這個協議

基協議

在這裡插入圖片描述

@required和optional關鍵字

在這裡插入圖片描述
具體寫法如下

#import <Foundation/ Foundation. h>
@protocol SportProtocol <NS0bject>
//方法宣告列表
//注意:如果沒有使用任何關鍵字修飾協議中的方法,那麼該方法預設就是required的
@required
	-(void)playFpotball;
	- (void) playBasketball;
	- (void) playBaseball;
@end

協議的應用場景

@protocal型別限制
在這裡插入圖片描述
在這裡插入圖片描述

//WifeCondition.h
#import <Foundation/Foundation. h>
@protocol WifeCondition <NSObject>
//會做飯
- (void) cooking;
//會洗衣服
- (void)washing;
//有一份好工作
- (void)job;
@end
//Wife.h
#import <Foundation/ Foundation. h>
#import "WifeCondition. h"
@interface Wife : NSObject <WifeCondition>
@end
//Wife.m
//注意此時妻子這個類只是遵守了這個協議,這個時候就算你在賦值的時候進行了型別的限制,但是也不會在編譯的時候發出警告,在執行呼叫的的時候還是會出錯。
@implementation Wife
@end
//People.h
#import <Foundation/Foundation. h>
#import "Wife. h"
@interface Person : NSObject
//媳婦
//注意:記住一點,型別限定是寫在資料型別的右邊的
@property (nonatomic, strong) Wife<WifeCondition> *wife;  //注意此時是妻子這個物件是要遵守這個協議
@end

//People.m
#import "Person. h"
@implementation Person
@end

//main.m
#import <Foundation/Foundation. h>
#import "Person. h"
#import "Wife. h"
#import "WifeCondition. h"
int main(int argc, const char * argv[]) 
{
	Person *p = [Person new] ;
	// 1. 協議的第一個應用場景,可以將協議寫在資料型別的右邊,明確的標註如果想給該變數賦值,那麼該物件必須遵守某個協議
	Wife<WifeCondition> *W = [Wife, new] ; 
	p.wife = w;

	//注意此時有第二個需要注意的地方
	//假如正如我在Wife.m裡面說的,雖然妻子有型別限制,但是假如妻子並沒有實現這寫方法那麼也是無法執行的
	p.wife.cooking();  //此時我們呼叫妻子類所遵守的協議的方法會發現執行出錯,所以我們在以後允許這種情況的時候需要進行判斷,判斷這個類到底有沒有實現這個協議

	// 注意:雖然在接受某-一個物件的時候,對這個物件進行了型別限定(限定它必須實現某個協議),但是並不意味著這個物件就真正的實現了該方法。所以每次在呼叫物件的協議方法時應該進行一次驗證
	if ([p.wife respondsToSelector :@selector(cooking)])  //如果實現了返回YES否則NO 
	{
		[p.wife.cooking];
	}
	if ([p. wife respondsToSelector: @selector(washing)]) 
	{
		[p.wife.washing];
	}
	if ([p.wife respondsToSelector:@selector(job)]) 
	{
		[p.wife.job] ;
	}
	return 0;
}
//在其子類中實現下面的方法上面的便可以正常執行
//Wife.m
#import "Wife. h"
@implementation Wife
//會做飯,
- (void) cooking
{
	NSLog(@"%s",__func__ );
}
//會洗衣服
- (void)washing
{
	NSLog(@%"s", __func__ );
}
//有一份好工作
- (void)job
{
	NSLog(@%"s", __func__ );
}

協議的第二個應用場景代理設計模式

在這裡插入圖片描述
其實就是類的組合,可以更加靈活的去處理一些事情,使程式碼重複率降低
就不仔細說明了。

相關文章