最近在輸出本身從大一到如今學習iOS開發過程當中的相關內容,但願你們star、fork支持!!! github.com/windstormey…java
這篇文章來梳理一番在iOS頁面間的傳值方式,說到頁面傳值,無論在任何平臺開發中都是一個很是的重要的事情,這就讓我想起了當初大一那會兒對Qt還不夠熟悉,竟然對兩個Window之間的傳值用了一個全局變量來實現,而後在其它Window中顯式聲明一個extern標記變量做爲數據源。emmm,如今想起來當初真的是蠢潔又扇涼。git
在iOS中的頁面傳值方式主要如下六種:程序員
在以上六種傳值方式中,一、三、4使用得最爲普遍,並且普遍存在與iOS SDK和各類第三方庫中。github
屬性傳值不太經常使用於頁面傳值,由於經過屬性傳值有些時候須要顧及到各個頁面的調用時機次序,反而經過屬性設置一些頁面的特殊功能標識的做用會更大。設計模式
舉個🌰🍐!!!咱們首先要進行跳轉的第二個頁面設置屬性值propertyPassValue。ruby
@interface NextViewController : UIViewController
@property (nonatomic, copy) NSString* propertyPassValue;
@end
複製代碼
重寫propertyPassValue的set方法,屬性傳值的寫法還有其它方式,若是咱們對相關的對象作了lazy load,那麼咱們應該在lazy load block中進行賦值。閉包
- (void)setPropertyPassValue:(NSString *)propertyPassValue {
_propertyPassValue = propertyPassValue;
self.tipsLabel.text = propertyPassValue;
}
複製代碼
此時咱們經過實例化NextViewController,經過push或者present模態推出該vc便可看到對應的Label顯示出propertyPassValue內容。函數
單例傳值,說是傳值實際上應該說是利用了單例的「持久化」狀態來達到持久化屬性的一種方法,單例應該說是一種設計模式,單例是一把很是好使的「武器」,放在會使的人手裏,單例會變成一把利劍,可以很好的解決跨多個頁面的值傳遞問題,可是若是放在不熟悉的人手裏,頗有可能給他這個一高整個項目就散發着弄弄的java味了。🙂。(關於設計模式還會有一篇文章專門對其作了總結,你們能夠持續關注。)工具
單例從字面上來理解,確定是不一樣於往先alloc再init一個對象,最多見的用法就是NSUserDefault。(這個後邊再說)post
+ (instancetype)shareInstance {
static instanceModel *model = nil;
if (!model) {
model = [[instanceModel alloc] init];
}
return model;
}
複製代碼
經過以上代碼咱們就建立出來了一個單例,實際上單例就是利用了static標識符告訴Xcode這個instanceModel變量要放在靜態存儲區,靜態存儲區是內存在程序編譯的時候就已經分配好的,這塊內存在App整個運行期間都將存在。它主要存放靜態數據、全局數據和常量。
instanceModel是單拎出來的一個NSObject對象,以上代碼只是建立出來一個存在整個App運行期間的單例,當咱們把App從後臺kill掉後,整個單例也就不存在了,由於此時App的運行期已經結束了,若是你要持久化數據那就得使用其它方法(好比存文件)。
self.tipsLabel.text = [instanceModel shareInstance].instanceString;
複製代碼
instanceString是多加的一個NSString對象,咱們經過其起到傳值的做用。
NSUserDefault是蘋果本來用於提供用戶偏好設置的,可是我大天朝神奇的程序員怎麼可能聽命與你?因此在實際開發中NSUserDefault有時候起到了鬼斧神工的做用,而且咱們還可使用它來做爲頁面之間傳值的工具。
// 設置NSUserDefault對應的k-v
[[NSUserDefaults standardUserDefaults] setObject:@"NSUserDefaults傳值" forKey:@"NSUserDefaults"];
// 數據同步
[[NSUserDefaults standardUserDefaults] synchronize];
複製代碼
而使用NSUserDefault也很是的簡單,
self.tipsLabel.text = [[NSUserDefaults standardUserDefaults] objectForKey:@"NSUserDefaults"];
複製代碼
NSUserDefault底層是個文件,被蘋果存在了對應的App沙盒中,只有咱們去使用過它纔會出如今沙盒中。(沙盒實際上就是對應的App文件夾)所以除非咱們把這個App刪掉和手動清洗掉對應的數據,不然咱們對NSUserDefault設置的value將一直存在,同時它也是數據持久化的一直方式。在使用NSUserDefault做爲頁面間傳值的工具時,千萬要注意記得要手動顯式調用NSUserDefault的數據同步方法,並且使用的時候儘可能避免同時對一個key進行同時「寫」操做。(不過也沒人這麼無聊對一個用戶偏好key同時寫吧?🙂)
代理傳值我能夠拍着胸脯說這絕對是目前iOS中使用範圍最廣和使用次數最多的傳值方式,並且基本上每個VC的編寫都會接觸到代理,並且代理傳值對於我本身來講也是一個很是經常使用的反向傳值方式。
同時代理也是一種設計模式,第一次接觸代理模式的同窗(包括我)都會有些摸不着頭腦,雖說是和平常生活中找中介租/買房子很是像,可是轉換到代碼層面就是迷迷糊糊。🙂。先po一張圖,
從上圖中咱們能夠看到,需求方至關因而代理制定者,而受理方則是代理實現者(說得我都懵逼了😂)。直接瞅代碼吧,
// 協議
@protocol NextViewControllerDelegate <NSObject>
- (void)passValueOfProtocol:(NSString *)string;
@end
@interface NextViewController : UIViewController
@property (nonatomic, weak) id<NextViewControllerDelegate> vcDelegate;
@end
複製代碼
從以上代碼中,咱們看到了制定代理使用@protocol關鍵字便可,@protocol中的內容能夠認爲是「合同內容」,咱們還須要聲明一個id指針變量vcDelegate,這個變量能夠認爲是「合同書」自己,而如何讓需求方發佈這個協議或者合同呢?
[_vcDelegate passValueOfProtocol:self.tipsLabel.text];
複製代碼
在須要的地方經過id指針變量_vcDelegate調用passValueOfProtocol方法便可,參數即爲要填入合同書的內容🙂。經過顯式調用以上方法便可完成所謂的「填寫合同書」環節。接下來,咱們要在對應的類中的遵照協議(合同書)便可使用需求方填入合同書中的內容。
// 寫明要遵照的協議
@interface ViewController () < NextViewControllerDelegate>
@end
複製代碼
NextViewController *vc = [[NextViewController alloc] init];
// 寫明要受理的代理對象
vc.vcDelegate = self;
複製代碼
// 要實現的代理方法
- (void)passValueOfProtocol:(NSString *)string {
self.tableView.headTitleLabel.text = string;
}
複製代碼
受理方遵照協議的過程千萬要注意設置代理對象,vc.vcDelegate = self;
,若是你忘了設置對應的代理對象,而只是實現了協議方法,這就至關於咱們把合同給了對方而已,對方並未簽名,這份合同對甲乙雙方其實是無效的!所以千萬別忘了設置代理對象。經過以上步驟,咱們便可從需求方拿到數據,從而顯示在代理方,是一種很是有效(可是有些麻煩)的反向傳值方法。
block,是蘋果這幾年來強烈推薦的回調傳值方式。你能夠認爲是C中匿名函數,C++/java中的lambda表達式,JS中的閉包等。只不過在iOS中換了個說法稱之爲block(我是這麼認爲的🙂)。
@interface NextViewController : UIViewController
@property (nonatomic, copy) NSString* propertyPassValue;
@property (nonatomic, weak) id<NextViewControllerDelegate> vcDelegate;
@property (nonatomic, copy) void (^passValueBlock)(NSString *);
@end
複製代碼
在此咱們把block做爲了一個屬性,而賦值block,
self.passValueBlock(self.tipsLabel.text);
複製代碼
咱們只須要在對應的地方給block傳入NSString類型參數便可。而使用block,咱們在對應的VC中參考如下代碼便可獲取到對應的值。
NextViewController *vc = [[NextViewController alloc] init];
vc.passValueBlock = ^(NSString *string) {
self.tableView.headTitleLabel.text = string;
};
複製代碼
以上只是block的簡單使用,關於block更加細節的一些用法,你們能夠參考這篇文章(操蛋的block語法🙂)。
通知也是一把利器,尤爲是涉及跨多個頁面間的傳值時它的好處就提現出來了,並且最重要的是在項目中巧妙的使用通知可以打破以往縱向傳遞的繁雜性,從而達到一種「星型」發射狀的模型。之因此這麼說是由於通知能夠說是最切合真正的面向對象的核心,真正的面嚮對象語言——smalltalk,若是沒記錯的話,smalltalk應該是一切面向對象語言的開山鼻祖吧😀。面向對象的真正核心應該是「一切操做皆消息」,也就是說,就算是簡單加法、減法也是經過「發送消息」完成的,而不是像各類課本及老師說的什麼繼承、多態等等這些外殼,這些外殼用C也可以實現,根本就不算是面嚮對象語言的標誌,反觀如今好比C++、java這些當初跟smalltalk分道揚鑣的語言,現現在的更新也都是在愈來愈像smalltalk罷了。(包括ruby🙂),OC就是基於smalltalk的封裝,不過不少東西也沒拿完。
而在iOS中實現通知得益於蘋果爸爸的高度封裝能力,把不少問題都給咱們搞定了,
[[NSNotificationCenter defaultCenter] postNotificationName:@"notify" object:nil userInfo:@{@"notify" : self.tipsLabel.text}];
複製代碼
經過以上代碼,咱們就註冊了一個名爲notify的通知,其帶有了一個key爲notify且value爲self.tipsLabel.text的字典。使用通知須要有一個通知中心做爲溝通的橋樑,發送通知的object爲告訴通知中心要把這個通知消息發送給哪一個對象,若是想要羣發消息通知,那就nil。
而接收通知,咱們須要給當前類添加一個觀察者,用於監聽對應name的通知。
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(notifyMesg:) name:@"notify" object:nil];
複製代碼
以上代碼中的object參數爲接受哪一個對象發送而來的消息,若是咱們想接收任何對象發送而來的消息那就nil吧。接着,咱們在處理消息通知的notifyMesg:方法中取得消息實體,
- (void)notifyMesg:(NSNotification *)notify {
self.tableView.headTitleLabel.text = notify.userInfo[@"notify"];
}
複製代碼
固然,咱們對當前類添加了觀察者去監聽某個通知,那就要在適當的實際去取消監聽,固然你徹底能夠不用移除通知,可是若是多個消息通知重疊在一個項目中時,很容易就致使消息的監聽迷之問題,由於一旦上升到通知層面的debug那就不是線性的了,可想而知難度得有多大。🙂。通常來講咱們會在當前類的dealloc方法中移除通知。