版權(quán)聲明:本文為作者原創(chuàng)書籍。轉(zhuǎn)載請注明作者和出處亚脆,未經(jīng)授權(quán),嚴禁私自轉(zhuǎn)載盲泛,侵權(quán)必究1舫帧!寺滚!
情感語錄: 不要去追一匹馬柑营,用追馬的時間種草,待到春暖花開時村视,就會有一群駿馬任你挑選官套;豐富自己,比取悅他人更有力量!
歡迎來到本章節(jié)奶赔,上一章節(jié)介紹了常用對話框和進度條
的使用惋嚎,知識點回顧 戳這里 Flutter基礎(chǔ)第十一章
本章知識點主要介紹 Flutter 中的數(shù)據(jù)共享和持久化,很顯然站刑,本章知識點相當重要另伍,數(shù)據(jù)共享和持久化操作基本是每個應(yīng)用的必備操作。比如:一個應(yīng)用里有很多界面都會用到我的的用戶信息绞旅,而我的用戶信息一開始就在登錄時就返回了摆尝,我們把用戶的基本信息存儲在一個全局的變量里即可實現(xiàn)數(shù)據(jù)共享,讓其他視圖直接引用該變量值即可 因悲。但是這樣的存儲只能達到內(nèi)存級別的堕汞,應(yīng)用關(guān)閉后這些數(shù)據(jù)就被丟失了。要實現(xiàn)關(guān)閉應(yīng)用后再次重啟應(yīng)用我的用戶信息而不丟失那該怎么辦呢晃琳? 這就是本章節(jié)的重點了------ 持久化
本章簡要:
1讯检、Flutter 中單例介紹
2、shared_preferences 實現(xiàn)數(shù)據(jù)持久化
3蝎土、IO 文件讀寫操作數(shù)據(jù)持久化
一视哑、全局靜態(tài)變量
Flutter 中的 Dart 語言如同 Java 語言一樣,存在很多地方的相似性誊涯。在通過使用 static
關(guān)鍵字 為一個變量開辟內(nèi)存地址后挡毅,而其他需要引用到該變量值時, 只需要將內(nèi)存地址指向該變量即可實現(xiàn)數(shù)據(jù)共享暴构。
如: 我定義了一個 Global 類來管理全局的的靜態(tài)資源:
class Global{
//定義一個靜態(tài)屬性 name
static String name = "張三";
}
在需要使用到該變量值的地方 通過 Global.name
方式獲取該變量的值跪呈。
二、單例
單例模式是日常開發(fā)中最常用的設(shè)計模式之一取逾。Dart 是單線程模型耗绿,因此在Flutter 中實現(xiàn)單例 不需要像 Java 中加雙重檢查鎖去考慮多線程的問題。
Dart中的單例也有兩種模式:
1砾隅、餓漢式
餓漢式比較好理解:在類加載時误阻,就進行實例的創(chuàng)建。加載時獲取實例速度較慢晴埂,運行時速度較快究反。通俗的講:"我管你吃不吃包子,反正我先把這坨包子蒸好放在這兒"
實例代碼:
class UserHelper{
// 單例公開訪問點
factory UserHelper() =>_userInstance();
static UserHelper get instance => _userInstance();
// 靜態(tài)私有成員儒洛,沒有初始化
static UserHelper _instance = UserHelper._();
// 私有構(gòu)造函數(shù)
UserHelper._() {
// 具體初始化代碼
print("------>初始化");
}
// 靜態(tài)精耐、同步、私有訪問點
static UserHelper _userInstance() {
return _instance;
}
String getUserName(){
return "張三";
}
}
餓漢式單例測試:
void main(){
var userHelper = UserHelper();
var userHelper1 = UserHelper();
var userHelper2 = UserHelper.instance;
print("------------> 對象:'${userHelper.hashCode} 相等 '+${identical(userHelper, userHelper1)}"); //true
print("------------> 對象:'${userHelper1.hashCode} 相等 '+${identical(userHelper, userHelper2)}"); //true
print("------------> 對象:'${userHelper2.hashCode} 相等 '+${identical(userHelper1, userHelper2)}"); //true
}
控制臺輸出:
I/flutter: ------>初始化
I/flutter: ------------> 對象:'1070008279 相等 '+true
I/flutter: ------------> 對象:'1070008279 相等 '+true
I/flutter: ------------> 對象:'1070008279 相等 '+true
可以看出 UserHelper 類有且只初始化了一次琅锻,且 hashCode 值也都相等卦停,證明后面的無論 new UserHelper() 多少次拿到的都是同一對象向胡。
2、懶漢式
在類加載時惊完,不創(chuàng)建實例僵芹。加載時速度較快,運行時獲取實例速度較慢专执。通俗的講:“我管你吃不吃包子淮捆,等你問我了我才開始給你做包子”
實例代碼:
class UserHelper{
// 單例公開訪問點
factory UserHelper() =>_userInstance();
static UserHelper get instance => _userInstance();
// 靜態(tài)私有成員,沒有初始化
static UserHelper _instance;
// 私有構(gòu)造函數(shù)
UserHelper._() {
// 具體初始化代碼
print("------>初始化");
}
// 靜態(tài)本股、同步攀痊、私有訪問點
static UserHelper _userInstance() {
if (_instance == null) {
_instance = UserHelper._();
}
return _instance;
}
}
懶漢式單例測試:
void main(){
var userHelper = UserHelper();
var userHelper1 = UserHelper();
var userHelper2 = UserHelper.instance;
print("------------> 對象:'${userHelper.hashCode} 相等 '+${identical(userHelper, userHelper1)}"); //true
print("------------> 對象:'${userHelper1.hashCode} 相等 '+${identical(userHelper, userHelper2)}"); //true
print("------------> 對象:'${userHelper2.hashCode} 相等 '+${identical(userHelper1, userHelper2)}"); //true
}
控制臺輸出:
I/flutter ( 8120): ------>初始化
I/flutter ( 8120): ------------> 對象:'537698073 相等 '+true
I/flutter ( 8120): ------------> 對象:'537698073 相等 '+true
I/flutter ( 8120): ------------> 對象:'537698073 相等 '+true
無論是通過餓漢式 還是懶漢式方式實現(xiàn)的單例始終拿到的都是同一對象,而同一對象中的屬性值肯定是相等的拄显,那么上面介紹的第一點通過靜態(tài)實現(xiàn)全局共享 也可以換成單例的方式實現(xiàn)苟径。
下面列舉下使用單例的場景和好處:
1、對象需要頻繁的實例化和銷毀躬审,此時考慮使用單例可以大幅度提高性能棘街。
2、控制資源的使用承边。
3遭殉、控制實例產(chǎn)生的數(shù)量,達到節(jié)約資源的目的博助。
4险污、作為通信媒介使用,也就是數(shù)據(jù)共享富岳。
三蛔糯、shared_preferences 數(shù)據(jù)持久化
shared_preferences 是 Flutter 提供的 key-value 存儲插件,它通過Android和iOS平臺提供的機制來實現(xiàn)數(shù)據(jù)持久化到磁盤中窖式。在 iOS 上封裝的是 NSUserDefault(后綴 .plist
的文件中)蚁飒, 在 android 上封裝的是 SharedPreferences(后綴.xml
文件中)。在使用上也是如同原生一樣簡單萝喘。
為工程添加 shared_preferences 插件:
1淮逻、在pubspec.yaml
文件中添加依賴
dependencies:
fluttertoast: ^3.0.3
flutter:
sdk: flutter
#添加持久化插件 sp
shared_preferences: ^0.5.3+1
在 pubspec.yaml 添加依賴時特別需要注意縮進,多一個或少一個空格可能都將添加不上阁簸。
2弦蹂、安裝依賴庫
執(zhí)行 flutter packages get
命令;AS 開發(fā)工具直接右上角 packages get
也可强窖。
3、在使用的文件中導入該庫
import 'package:shared_preferences/shared_preferences.dart';
shared_preferences 源碼分析:
class SharedPreferences {
SharedPreferences._(this._preferenceCache);
static const String _prefix = 'flutter.';
static SharedPreferences _instance;
static Future<SharedPreferences> getInstance() async {
if (_instance == null) {
final Map<String, Object> preferencesMap =
await _getSharedPreferencesMap();
_instance = SharedPreferences._(preferencesMap);
}
return _instance;
}
/// The cache that holds all preferences.
///
/// It is instantiated to the current state of the SharedPreferences or
/// NSUserDefaults object and then kept in sync via setter methods in this
/// class.
///
/// It is NOT guaranteed that this cache and the device prefs will remain
/// in sync since the setter method might fail for any reason.
final Map<String, Object> _preferenceCache;
/// Returns all keys in the persistent storage.
Set<String> getKeys() => Set<String>.from(_preferenceCache.keys);
/// Reads a value of any type from persistent storage.
dynamic get(String key) => _preferenceCache[key];
/// Reads a value from persistent storage, throwing an exception if it's not a
/// bool.
bool getBool(String key) => _preferenceCache[key];
/// Reads a value from persistent storage, throwing an exception if it's not
/// an int.
int getInt(String key) => _preferenceCache[key];
/// Reads a value from persistent storage, throwing an exception if it's not a
/// double.
double getDouble(String key) => _preferenceCache[key];
/// Reads a value from persistent storage, throwing an exception if it's not a
/// String.
String getString(String key) => _preferenceCache[key];
/// Returns true if persistent storage the contains the given [key].
bool containsKey(String key) => _preferenceCache.containsKey(key);
/// Reads a set of string values from persistent storage, throwing an
/// exception if it's not a string set.
List<String> getStringList(String key) {
List<Object> list = _preferenceCache[key];
if (list != null && list is! List<String>) {
list = list.cast<String>().toList();
_preferenceCache[key] = list;
}
// Make a copy of the list so that later mutations won't propagate
return list?.toList();
}
/// Saves a boolean [value] to persistent storage in the background.
///
/// If [value] is null, this is equivalent to calling [remove()] on the [key].
Future<bool> setBool(String key, bool value) => _setValue('Bool', key, value);
/// Saves an integer [value] to persistent storage in the background.
///
/// If [value] is null, this is equivalent to calling [remove()] on the [key].
Future<bool> setInt(String key, int value) => _setValue('Int', key, value);
/// Saves a double [value] to persistent storage in the background.
///
/// Android doesn't support storing doubles, so it will be stored as a float.
///
/// If [value] is null, this is equivalent to calling [remove()] on the [key].
Future<bool> setDouble(String key, double value) =>
_setValue('Double', key, value);
/// Saves a string [value] to persistent storage in the background.
///
/// If [value] is null, this is equivalent to calling [remove()] on the [key].
Future<bool> setString(String key, String value) =>
_setValue('String', key, value);
/// Saves a list of strings [value] to persistent storage in the background.
///
/// If [value] is null, this is equivalent to calling [remove()] on the [key].
Future<bool> setStringList(String key, List<String> value) =>
_setValue('StringList', key, value);
/// Removes an entry from persistent storage.
Future<bool> remove(String key) => _setValue(null, key, null);
Future<bool> _setValue(String valueType, String key, Object value) {
final Map<String, dynamic> params = <String, dynamic>{
'key': '$_prefix$key',
};
if (value == null) {
_preferenceCache.remove(key);
return _kChannel
.invokeMethod<bool>('remove', params)
.then<bool>((dynamic result) => result);
} else {
if (value is List<String>) {
// Make a copy of the list so that later mutations won't propagate
_preferenceCache[key] = value.toList();
} else {
_preferenceCache[key] = value;
}
params['value'] = value;
return _kChannel
.invokeMethod<bool>('set$valueType', params)
.then<bool>((dynamic result) => result);
}
}
/// Always returns true.
/// On iOS, synchronize is marked deprecated. On Android, we commit every set.
@deprecated
Future<bool> commit() async => await _kChannel.invokeMethod<bool>('commit');
/// Completes with true once the user preferences for the app has been cleared.
Future<bool> clear() async {
_preferenceCache.clear();
return await _kChannel.invokeMethod<bool>('clear');
}
/// Fetches the latest values from the host platform.
///
/// Use this method to observe modifications that were made in native code
/// (without using the plugin) while the app is running.
Future<void> reload() async {
final Map<String, Object> preferences =
await SharedPreferences._getSharedPreferencesMap();
_preferenceCache.clear();
_preferenceCache.addAll(preferences);
}
static Future<Map<String, Object>> _getSharedPreferencesMap() async {
final Map<String, Object> fromSystem =
await _kChannel.invokeMapMethod<String, Object>('getAll');
assert(fromSystem != null);
// Strip the flutter. prefix from the returned preferences.
final Map<String, Object> preferencesMap = <String, Object>{};
for (String key in fromSystem.keys) {
assert(key.startsWith(_prefix));
preferencesMap[key.substring(_prefix.length)] = fromSystem[key];
}
return preferencesMap;
}
/// Initializes the shared preferences with mock values for testing.
///
/// If the singleton instance has been initialized already, it is automatically reloaded.
@visibleForTesting
static void setMockInitialValues(Map<String, dynamic> values) {
_kChannel.setMockMethodCallHandler((MethodCall methodCall) async {
if (methodCall.method == 'getAll') {
return values;
}
return null;
});
_instance?.reload();
}
}
從源碼上看非常簡單削祈,首先 SharedPreferences 使用的一個單例模式翅溺,且是異步的脑漫。它在數(shù)據(jù)存儲上提供了:setInt、setBool咙崎、setString 和 setStringList
方法來設(shè)置特定類型的數(shù)據(jù)优幸。在數(shù)據(jù)讀取上提供了:getString 、getInt 褪猛、 getDouble网杆、getStringList
方法來獲取數(shù)據(jù)。此外還提供了如下幾個工具API:
1伊滋、是否包含有該 key 值從存儲:containsKey(String key)
2碳却、移除指定的 key 值存儲:remove(String key)
3、清除全部的持久化數(shù)據(jù):clear()
4笑旺、提交存儲數(shù)據(jù):commit()
昼浦,該方法已被廢棄。
綜合運用:
首先我們在 全局類 Global 中初始化 SharedPreferences 筒主,使用靜態(tài)方式方便其他地方使用关噪。
import 'package:shared_preferences/shared_preferences.dart';
class Global{
//定義一個全局的 sp
static SharedPreferences preferences;
//初始化
static void initPreferences() async{
preferences= await SharedPreferences.getInstance();
}
}
特別注意:SharedPreferences單例的獲取是一個異步方法所以需要 await 、async
關(guān)鍵字乌妙。
其次:在程序啟動時就初始化
void main(){
runApp(MyApp());
//初始化 sp
Global.initPreferences();
}
下面就可以在使用的地方直接引用即可使兔,例子:
import 'package:flutter/material.dart';
import 'package:flutter_learn/util/Global.dart';
import 'package:flutter_learn/util/ToastUtil.dart';
import 'package:shared_preferences/shared_preferences.dart';
//使用初始化好的靜態(tài)資源
SharedPreferences sharedPreferences = Global.preferences;
//定義一個內(nèi)存級別的變量
int count = 0;
class PersistenPage extends StatefulWidget {
PersistenPage({Key key}) : super(key: key);
_PersistenPageState createState() => _PersistenPageState();
}
class _PersistenPageState extends State<PersistenPage> {
@override
Widget build(BuildContext context) {
count = sharedPreferences.getInt("count") == null
? 0
: sharedPreferences.getInt("count");
return Scaffold(
appBar: AppBar(
title: Text("持久化"),
),
body: Container(
color: Colors.blue,
width: double.infinity,
child: Column(
children: <Widget>[
SizedBox(height: 20),
Text('ShardPreferences方式持久化',
style: TextStyle(color: Colors.white)),
SizedBox(height: 20),
Text("當前累計數(shù)據(jù):$count"),
Row(
children: <Widget>[
SizedBox(width: 5),
RaisedButton(
color: Colors.deepPurple,
elevation: 20,
focusElevation: 40,
child: Text('自增', style: TextStyle(color: Colors.white)),
onPressed: () {
setState(() {
count++;
sharedPreferences?.setInt("count", count);
});
},
),
SizedBox(width: 5),
RaisedButton(
color: Colors.deepPurple,
elevation: 20,
focusElevation: 40,
child: Text('清除', style: TextStyle(color: Colors.white)),
onPressed: () {
setState(() {
count = 0;
sharedPreferences.clear();
// sharedPreferences.remove("count");
});
},
),
SizedBox(width: 5),
RaisedButton(
color: Colors.deepPurple,
elevation: 20,
focusElevation: 40,
child: Text('是否包含', style: TextStyle(color: Colors.white)),
onPressed: () {
setState(() {
count = 0;
bool flag = sharedPreferences.containsKey("count");
ToastUtil.show("是否包含: $flag");
});
},
),
SizedBox(width: 5),
],
),
SizedBox(height: 10),
],
),
));
}
}
案例效果圖:
從上面可以看出,無論是退出當前界面藤韵,還是關(guān)閉應(yīng)用后虐沥,再次回到持久化界面任然可以讀取到存儲到文件中的值,也就是實現(xiàn)了數(shù)據(jù)本地持久化荠察。
四置蜀、IO 文件讀寫操作數(shù)據(jù)持久化
Flutter 同原生一樣支持對文件的讀和寫。Flutter 中 IO 其實是 Dart 中的一部分悉盆,由于Dart VM是運行在PC或服務(wù)器操作系統(tǒng)下盯荤,而Flutter是運行在移動操作系統(tǒng)中,他們的文件系統(tǒng)所以會有一些差異焕盟。為此 PathProvider 插件提供了一種平臺透明的方式來訪問設(shè)備文件系統(tǒng)上的常用位置秋秤。該類當前支持訪問兩個文件系統(tǒng)位置:
1、臨時文件夾
:可以使用 getTemporaryDirectory() 來獲取臨時目錄脚翘;在 iOS 上對用的是 NSCachesDirectory 在 Android 對用的是 getCacheDir()灼卢。
2、應(yīng)用的Documents 目錄
:可以使用 getApplicationDocumentsDirectory 來獲取 Documents 目錄来农;在 iOS 對應(yīng)的是 NSDocumentDirectory 鞋真,在 Android 上對應(yīng)的是 AppData 目錄。
【特別注意:】臨時文件夾在執(zhí)行系統(tǒng)的清空緩存時會清空該文件夾沃于,documents 目錄只有在刪除應(yīng)用時才會清空涩咖。
PathProvider和 SharedPreferences 一樣需要引入海诲;具體步驟如下:
1、在pubspec.yaml
文件中添加聲明:
dependencies:
fluttertoast: ^3.0.3
flutter:
sdk: flutter
#添加持久化插件 sp
shared_preferences: ^0.5.3+1
#添加文件庫
path_provider: ^1.2.0
2檩互、安裝依賴庫
執(zhí)行 flutter packages get
命令特幔;AS 開發(fā)工具直接右上角 packages get
也可。
3闸昨、在使用的文件中導入該庫
import 'package:path_provider/path_provider.dart';
File類的代碼比較多蚯斯,這里就不貼內(nèi)部源碼了,下面只列出開發(fā)中常用的 API :
1饵较、create()
創(chuàng)建文件,多級目錄 recursive 為真則遞歸創(chuàng)建
2拍嵌、createSync()
同步方式創(chuàng)建文件
3、rename()
文件重命名
4告抄、renameSync()
同步設(shè)置文件重命名
5撰茎、copy()
復制文件
6、copySync()
同步復制文件
7打洼、length()
獲取文件長度
8龄糊、lengthSync()
同步獲取文件長度
9、absolute
獲取絕對路徑文件
10募疮、lastAccessed()
獲取最后一次的訪問時間
11炫惩、lastAccessedSync()
同步獲取最后一次的訪問時間
12、setLastAccessed()
設(shè)置最后一次的訪問時間
13阿浓、setLastAccessedSync()
同步設(shè)置最后一次的訪問時間
14他嚷、lastModified()
獲取最后一次修改時間
15、lastModifiedSync()
同步獲取最后一次修改時間
16芭毙、setLastModified()
設(shè)置最后一次修改時間
17筋蓖、setLastModifiedSync()
同步設(shè)置最后一次修改時間
18、open()
打開文件有多種方式和文件的訪問模式退敦,詳情參閱源碼
19粘咖、readAsBytes()
讀取字節(jié)
20、readAsBytesSync()
同步讀取字節(jié)
21侈百、readAsString()
讀取字符串
22瓮下、readAsStringSync()
同步讀取字符串
23、readAsLines()
讀取一行
24钝域、readAsLinesSync()
同步讀取一行
25讽坏、writeAsBytes()
寫出字節(jié)數(shù)組
26、writeAsBytesSync()
同步寫出字節(jié)數(shù)組
27例证、writeAsString()
寫出字符串
28路呜、writeAsStringSync()
同步寫出字符串
對 File 的操作有太多 API 了 ,更多內(nèi)容只有自己去查閱源碼了织咧。下面我們來將之前的計數(shù)器實例來改造下拣宰,使用文件的方式党涕。
首先我們在 全局類 Global 中創(chuàng)建文件 ,使用靜態(tài)方式方便其他地方使用巡社。
import 'dart:io';
import 'package:path_provider/path_provider.dart';
import 'package:shared_preferences/shared_preferences.dart';
class Global{
//定義一個全局的 sp
static SharedPreferences preferences;
static File file;
//初始化
static void initPreferences() async{
preferences= await SharedPreferences.getInstance();
}
//初始化一個文件,方便使用
static void initFile() async{
final directory = await getApplicationDocumentsDirectory();
if (!(file is File)) {
final String path = directory.path;
file = File('$path/myInfo.txt');
if( !file.existsSync()){
// 不存在則創(chuàng)建文件
file.createSync(recursive: true);
}
}
}
}
其次:在程序啟動時就初始化
void main(){
runApp(MyApp());
//初始化 sp
Global.initPreferences();
//初始化文件
Global.initFile();
}
下面就可以在使用的地方直接引用即可手趣,例子:
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter_learn/util/Global.dart';
import 'package:flutter_learn/util/ToastUtil.dart';
import 'package:shared_preferences/shared_preferences.dart';
//使用初始化好的靜態(tài)資源
SharedPreferences sharedPreferences = Global.preferences;
//使用初始化好的本地文件
File localFile = Global.file;
class PersistenPage extends StatefulWidget {
PersistenPage({Key key}) : super(key: key);
_PersistenPageState createState() => _PersistenPageState();
}
class _PersistenPageState extends State<PersistenPage> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("持久化"),
),
body: SingleChildScrollView(
child: Container(
color: Colors.blue,
height: 1000,
child: Column(
children: <Widget>[
// shardPreferences(),
IoReadWirte()
],
),
),
));
}
//文件的讀寫
Widget IoReadWirte(){
//定義一個內(nèi)存級別的變量
String info = localFile.readAsStringSync();
int count =int.parse(info??0);
return Column(
children: <Widget>[
SizedBox(height: 20),
Text('文件操作方式持久化',
style: TextStyle(color: Colors.white)),
SizedBox(height: 20),
Text("當前累計數(shù)據(jù):$count"),
Row(
children: <Widget>[
SizedBox(width: 5),
RaisedButton(
color: Colors.deepPurple,
elevation: 20,
focusElevation: 40,
child: Text('自增', style: TextStyle(color: Colors.white)),
onPressed: () {
setState(() {
count++;
localFile.writeAsStringSync(count.toString());
});
},
),
SizedBox(width: 5),
RaisedButton(
color: Colors.deepPurple,
elevation: 20,
focusElevation: 40,
child: Text('自減', style: TextStyle(color: Colors.white)),
onPressed: () {
setState(() {
count--;
localFile.writeAsStringSync(count.toString());
});
},
),
],
),
SizedBox(height: 10),
],
);
}
}
為了方便起見和減少文章篇幅晌该,這里只貼出了 File 操作的全部代碼,shardPreferences 中的代碼暫時刪除绿渣,文末的工程實例中擁有全部代碼朝群。
效果如下:
無論是用 SharedPreferences 還是文件操作 方式實現(xiàn)數(shù)據(jù)持久化都是非常簡單,在開發(fā)中 如果只是存儲一些簡單數(shù)據(jù) 其實用 SharedPreferences 就非常好了中符,如果涉及到一些文件(如:圖片姜胖,文檔等)才會去使用 File。
上面兩點都只是對一些單一數(shù)據(jù)的持久化淀散,而要持久化關(guān)系型數(shù)據(jù)就顯現(xiàn)的捉衿見肘了右莱,對于有關(guān)系的數(shù)據(jù)我們首先想到的肯定是通過數(shù)據(jù)庫去存儲,而數(shù)據(jù)庫
在開發(fā)中也是重中之中档插,涉及到篇幅問題這里就不介紹了慢蜓,這也是為下一篇文章做鋪墊吧
本章中很多都是通過靜態(tài)的方式去存值,其實在開發(fā)中應(yīng)該減少這種方式的使用 即在需要使用的時候才去獲取郭膛,避免不必要的內(nèi)存消耗晨抡!我這里只是為了方便演示才這樣寫。
好了本章節(jié)到此結(jié)束则剃,又到了說再見的時候了耘柱,如果你喜歡請留下你的小紅星,創(chuàng)作真心也不容易棍现;你們的支持才是創(chuàng)作的動力调煎,如有錯誤,請熱心的你留言指正, 謝謝大家觀看轴咱,下章再會 O(∩_∩)O