FOUNDATION_EXPORT 或#define 或 extern

本文探讨了在Objective-C中定义常量的不同方法,包括使用FOUNDATION_EXPORT与#define的区别,并解释了为什么直接比较指针地址比逐字符比较字符串更高效。
//我的.h文件

FOUNDATION_EXPORT NSString * const kMyConstantString;  
FOUNDATION_EXPORT NSString * const kMyConstantString2;


//.m文件是这样定义的

NSString * const kMyConstantString = @"Hello";
NSString * const kMyConstantString2 = @"World";

//还有一种是常用的#define方法了
#define kMyConstantString @"Hello"

应该使用FOUNDATION_EXPORT还是#define来定义常量?


区别:

使用第一种方法在检测字符串的值是否相等的时候更快.

对于第一种你可以直接使用(stringInstance == MyFirstConstant)来比较,

而define则使用的是这种.([stringInstance isEqualToString:MyFirstConstant])

哪个效率高,显而易见了.

第一种直接比较的是指针地址,

而第二个则是一一比较字符串的每一个字符是否相等.

来源地址:点击打开链接




//TTGClass.h
extern NSString * const TTGClassWorkBeginNotification;

//TTGClass.m
NSString * const TTGClassWorkBeginNotification = @"TTGClassWorkBeginNotification";


k”开头的命名,也算是Apple的规范了吧,内部常量统一以“k”开头。



关键:

  • 头文件中只是做声明,不做定义。所以用extern表示常量在别处定义的,尽量隐藏细节。
  • 同类名做常量名字的开头,这个非常关键,因为Objective-C里面没有像Java那样的“包”机制,所以必须靠命名来划分不同的类,常量当然也要遵循这个规则。
  • NSString,确定的类型;const,表明常量;Notification后缀,表明用途。这些都是需要注意的。


备注:2015年10月29日修改

/** * Copyright (c) 2017 Parrot Drones SAS * Copyright (c) 2017 Aurelien Barre * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of the copyright holders nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef _RTSP_COMMON_H_ #define _RTSP_COMMON_H_ #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ /* To be used for all public API */ #ifdef RTSP_API_EXPORTS # ifdef _WIN32 # define RTSP_API __declspec(dllexport) # else /* !_WIN32 */ # define RTSP_API __attribute__((visibility("default"))) # endif /* !_WIN32 */ #else /* !RTSP_API_EXPORTS */ # define RTSP_API #endif /* !RTSP_API_EXPORTS */ #include <errno.h> #include <inttypes.h> #include <libpomp.h> /* RTSP methods */ enum rtsp_method_type { RTSP_METHOD_TYPE_UNKNOWN = 0, RTSP_METHOD_TYPE_OPTIONS, RTSP_METHOD_TYPE_DESCRIBE, RTSP_METHOD_TYPE_ANNOUNCE, RTSP_METHOD_TYPE_SETUP, RTSP_METHOD_TYPE_PLAY, RTSP_METHOD_TYPE_PAUSE, RTSP_METHOD_TYPE_TEARDOWN, RTSP_METHOD_TYPE_GET_PARAMETER, RTSP_METHOD_TYPE_SET_PARAMETER, RTSP_METHOD_TYPE_REDIRECT, RTSP_METHOD_TYPE_RECORD, }; #define RTSP_METHOD_FLAG_OPTIONS 0x00000001UL #define RTSP_METHOD_FLAG_DESCRIBE 0x00000002UL #define RTSP_METHOD_FLAG_ANNOUNCE 0x00000004UL #define RTSP_METHOD_FLAG_SETUP 0x00000008UL #define RTSP_METHOD_FLAG_PLAY 0x00000010UL #define RTSP_METHOD_FLAG_PAUSE 0x00000020UL #define RTSP_METHOD_FLAG_TEARDOWN 0x00000040UL #define RTSP_METHOD_FLAG_GET_PARAMETER 0x00000080UL #define RTSP_METHOD_FLAG_SET_PARAMETER 0x00000100UL #define RTSP_METHOD_FLAG_REDIRECT 0x00000200UL #define RTSP_METHOD_FLAG_RECORD 0x00000400UL /** * Transport definitions */ enum rtsp_delivery { RTSP_DELIVERY_MULTICAST = 0, RTSP_DELIVERY_UNICAST, }; enum rtsp_lower_transport { RTSP_LOWER_TRANSPORT_UDP = 0, RTSP_LOWER_TRANSPORT_TCP, RTSP_LOWER_TRANSPORT_MUX, }; /** * RTSP Range header definitions * see RFC 2326 chapter 12.29 */ enum rtsp_time_format { RTSP_TIME_FORMAT_UNKNOWN = 0, RTSP_TIME_FORMAT_NPT, RTSP_TIME_FORMAT_SMPTE, RTSP_TIME_FORMAT_ABSOLUTE, }; /* RTSP Normal Play Time (NPT), see RFC 2326 chapter 3.6 */ struct rtsp_time_npt { int now; int infinity; uint64_t sec; uint32_t usec; }; /* RTSP SMPTE Relative Timestamps, see RFC 2326 chapter 3.5 */ struct rtsp_time_smpte { int infinity; uint64_t sec; unsigned int frames; }; /* RTSP Absolute Time (UTC, ISO 8601), see RFC 2326 chapter 3.7 */ struct rtsp_time_absolute { int infinity; uint64_t sec; uint32_t usec; }; struct rtsp_time { enum rtsp_time_format format; union { struct rtsp_time_npt npt; struct rtsp_time_smpte smpte; struct rtsp_time_absolute absolute; }; }; struct rtsp_range { struct rtsp_time start; struct rtsp_time stop; uint64_t time; }; /** * RTSP header extensions */ struct rtsp_header_ext { const char *key; const char *value; }; #define RTSP_HEADER_EXT_PARROT_PROXY_SESSION "X-com-parrot-proxy-session" #define RTSP_HEADER_EXT_PARROT_LINK_TYPE "X-com-parrot-link-type" RTSP_API const char *rtsp_method_type_str(enum rtsp_method_type val); RTSP_API const char *rtsp_delivery_str(enum rtsp_delivery val); RTSP_API const char *rtsp_lower_transport_str(enum rtsp_lower_transport val); RTSP_API const char *rtsp_time_format_str(enum rtsp_time_format val); RTSP_API int rtsp_range_get_duration_us(const struct rtsp_range *range, int64_t *duration); static inline int rtsp_time_us_to_npt(uint64_t time_us, struct rtsp_time_npt *time_npt) { if (time_npt == NULL) return -EINVAL; time_npt->now = 0; time_npt->infinity = 0; time_npt->sec = time_us / 1000000; time_npt->usec = time_us - time_npt->sec * 1000000; return 0; } static inline int rtsp_time_npt_to_us(const struct rtsp_time_npt *time_npt, uint64_t *time_us) { if ((time_npt == NULL) || (time_us == NULL)) return -EINVAL; if ((time_npt->now) || (time_npt->infinity)) return -EINVAL; *time_us = time_npt->sec * 1000000 + time_npt->usec; return 0; } #ifdef __cplusplus } #endif /* __cplusplus */ #endif /* !_RTSP_COMMON_H_ */
08-05
在window10安装vs2013编译,调试程序 出现以下错误代码 warning C4251: “LogMgrSingleton::m_vecLogFileHandle”: class“std::map<std::string,FILE *,std::less<_Kty>,std::allocator<std::pair<const _Kty,_Ty>>>”需要有 dll 接口由 class“LogMgrSingleton”的客户端使用 warning C4251: “LogMgrSingleton::m_vecLogStream”: class“std::map<std::string,LogStream *,std::less<_Kty>,std::allocator<std::pair<const _Kty,_Ty>>>”需要有 dll 接口由 class“LogMgrSingleton”的客户端使用 给出解释并修正方案 下面是vs2013调试的程序原代码: #ifndef LOG_MGR_SINGLETON_H #define LOG_MGR_SINGLETON_H 1 //资源管理类 #include "../Memory/FreeList.h" #include "../TObjectPool.h" #include <memory> // 添加智能指针支持 class LogManagerImpl; // 前置声明 class LogStream; //!日志记录的条目信息结构体 struct _Foundation_Export_ LOG_ITEM { public: ////!操纵控制块 表的互斥变量 static Thread::RecMutex m_mutex; _Def_Pool_Factory_(LOG_ITEM); //!日志条目配置信息 enum ENLOGITEMCONFIG { //!最大日志长度 enMaxLogItemSize = 1024, //!最大名字长度 enMaxLogNameSize = 32, }; char m_typeName[enMaxLogNameSize+1]; char m_szDesc[enMaxLogItemSize+1]; unsigned int m_nNumber; public: //结构体赋值 int SetValue(const std::string& strType, const char* pszDesc, const unsigned int nNumber) { size_t cpySize = enMaxLogNameSize > strType.size() ? strType.size() : enMaxLogNameSize; memcpy(m_typeName,strType.c_str(),cpySize); m_typeName[cpySize] = 0; m_nNumber = sizeof(m_szDesc); if (m_nNumber > nNumber) { m_nNumber = nNumber; } memcpy(m_szDesc, pszDesc, m_nNumber); return true; }; LOG_ITEM& operator = (const LOG_ITEM& item) { memcpy(m_typeName,item.m_typeName,sizeof(m_typeName)); m_nNumber = sizeof(item.m_szDesc); if (m_nNumber > item.m_nNumber) { m_nNumber = item.m_nNumber; } memcpy(m_szDesc, item.m_szDesc, m_nNumber); return *this; } LOG_ITEM& operator = (const LOG_ITEM* pItem) { memcpy(m_typeName,pItem->m_typeName,sizeof(m_typeName)); m_nNumber = pItem->m_nNumber; memcpy(m_szDesc, pItem->m_szDesc, m_nNumber); return *this; } void Reset(void) { } }; /** * @brief Log性能参数记录 */ class _Foundation_Export_ LogPerfData { public: LogPerfData(void) :m_logTotalCount(0) { } //!总共记录的条目数量 unsigned int m_logTotalCount; protected: private: }; /** *日志记录,单件模式 */ class _Foundation_Export_ LogMgrSingleton { _Singleton_Decl(LogMgrSingleton); public: // Constructor LogMgrSingleton(); // Destructor virtual ~LogMgrSingleton(); //!性能参数记录 LogPerfData m_perf; //日志有关操作 public: //!注册一个日志类型 bool RegisterLog(std::string strType); //!获得LogItem,以便于外部直接改写Item中的数据,避免一次拷贝 LOG_ITEM* GetLogItem(void); //!直接报告LogItem int Report(LOG_ITEM* item); //!日志输出,放入列表 int Report(const std::string& strType, const char* pszDesc, const int lNumber); //!日志输出, int ReportEx(const std::string& strType, char* pszDesc, int lNumber); //!线程调用的函数 static void WriteLog(void* ptr); //!具体的处理Log的函数 void WriteLogEx(); //!依据注册名,查找log流 LogStream* LookupStreamByName(const std::string& name); //输出流有关操作 public: //!压入一个stream,以后创建流就用这个创建 void PushStream(LogStream* stream); //!获得一个预先压入的stream LogStream* CreateStream(const std::string& n); //!弹出一个stream LogStream* PopStream(void); //创建文件有关操作 private: //!创建logs文件夹 int CreateLogsDirectory(void* pContext); //!创建当前日期文件夹 int CreateDateDirectory(void* pContext); //!获得当前日期 int GetCurrentDate(char* pszReturn, const int nLength); //!文件是否存在 int IsFileExist(std::string& strPath); private: //!从日志队列m_queLogList中获得第一个Log. LOG_ITEM* GetFrontLogData(void); private: //存放日志文件的句柄 std::map<std::string, FILE*> m_vecLogFileHandle; std::map<std::string, LogStream*> m_vecLogStream; //std::unique_ptr<LogManagerImpl> pImpl; // Pimpl惯用法 //File Handle FILE* m_hFile; //logs文件夹 std::string m_strLogsPath; std::stack<LogStream*> _streamStack; AG_Time m_AG_Time; private: Thread::BaseThread* m_LogThread; ////!操纵控制块 表的互斥变量 Thread::RecMutex m_mutex; //!开始log的信号量 Thread::Semaphore m_sem; private: //!日志结构体 LOG_ITEM m_LogItem; //!存放所有日志的线性链表(目前使用queue代替) std::queue<LOG_ITEM*> m_queLogList; //!下面创建的内存对象,最好能放到 private: //!创建玩家对象 LOG_ITEM* CreateLogItem(void); //!删除玩家对象 void FreeLogItem(LOG_ITEM* pLogItem); private: //预存放日志的内存分配 gems::FreeList< LOG_ITEM, gems::CompactableChunkPolicy<LOG_ITEM>, gems::ConstantChunkSizeGrowthPolicy<LOG_ITEM, 1024> > m_logMemoryList; }; //! #define LOG_REPORT_PUSHSTREAM(ptr) LogMgrSingleton::Instance()->PushStream(ptr) #define LOG_REPORT_POPSTREAM() LogMgrSingleton::Instance()->PopStream() //! #define LOG_REPORT_REGISTER(path) LogMgrSingleton::Instance()->RegisterLog(path) #define LOG_REPORT_EX(path, desc, num) LogMgrSingleton::Instance()->Report(path, desc, num) #define LOG_REPORT_LOOKUP(name) LogMgrSingleton::Instance()->LookupStreamByName(name) extern "C" { void _Foundation_Export_ _LOG_REPORT_OUTPUT_(const char* pszType,const char* pszString,...); #define LOG_REPORT_OUTPUT void _Foundation_Export_ _Server_LOG_File_(const char* pszType,const char* pszString,...); void _Foundation_Export_ debugOutputString(const char* pszString,...); //void _Foundation_Export_ LOG_REPORT_OUTPUT2( ); } #endif
最新发布
11-07
在window10安装vs2013编译,调试程序 出现以下错误代码 warning C4251: “LogManagerSingleton::_streamStack”: class“std::stack<LogStream *,std::deque<_Ty,std::allocator<_Ty>>>”需要有 dll 接口由 class“LogManagerSingleton”的客户端使用 给出解释并修正方案 下面是代码: // Copyright (C) 1991 - 1999 Rational Software Corporation #if defined (_MSC_VER) && (_MSC_VER >= 1000) #pragma once #endif #ifndef _INC_LOGMANAGERSINGLETON_4112E32B0157_INCLUDED #define _INC_LOGMANAGERSINGLETON_4112E32B0157_INCLUDED #include <stack> //定义Enable操作是否可以Register新的LOG //#define _ENABLE_WITH_NEW class LogSwitchBox; class LogLeaf; class LogStream; class LogStreamSimpleFile; /** *日志记录管理者,单件模式 */ class _Foundation_Export_ LogManagerSingleton { public: LogSwitchBox* GetRoot() const; // Destructor virtual ~LogManagerSingleton(); bool RegisterLog(const std::string path ); //!压入一个stream,以后创建流就用这个创建 void PushStream(LogStream* stream); //!弹出一个stream LogStream* PopStream(void); static LogManagerSingleton* getInstance(); static void Cleanup(void); protected: LogStream* CreateStream(const std::string& name,const std::string& fullPath); // Constructor LogManagerSingleton(); LogSwitchBox* _root; std::stack<LogStream*> _streamStack; private: static LogManagerSingleton* uniqueInstance; public: int Report(const std::string path,std::string desc); int Report(const std::string path,std::string desc,long num); // 设置节点者叶子的状态 void Enable(const std::string path,bool enable); }; // #define LOG_PUSHSTREAM(ptr) LogManagerSingleton::getInstance()->PushStream(ptr) #define LOG_POPSTREAM() LogManagerSingleton::getInstance()->PopStream() #define LOG_REPORTNUM(path,desc,num) LogManagerSingleton::getInstance()->Report(path,desc,num) #define LOG_REPORT(path,desc) LogManagerSingleton::getInstance()->Report(path,desc) #define LOG_ENABLE(path,enable) LogManagerSingleton::getInstance()->Enable(path,enable) #define LOG_REGISTER(path) LogManagerSingleton::getInstance()->RegisterLog(path) #define LOG_CLEANUP() do{/*delete LogManagerSingleton::getInstance()*/LogManagerSingleton::Cleanup();}while(0) extern "C" { void _Foundation_Export_ LOG_REPORTVAR(const char* path,const char* pszString,...); void _Foundation_Export_ LOG_ASSERT(bool exp,const char* pszString,...); } extern "C" { void _Foundation_Export_ LOG_DEBUG_REPORT(const char* path,const char* pszString,...); } #endif /* _INC_LOGMANAGERSINGLETON_4112E32B0157_INCLUDED */ // Copyright (C) 1991 - 1999 Rational Software Corporation #include "stdafx.h" #include "LogManagerSingleton.h" #include "LogSwitchBox.h" #include "LogLeaf.h" #include "LogStream.h" #include "LogStreamSimpleFile.h" LogManagerSingleton* LogManagerSingleton::uniqueInstance=0; LogStream* LogManagerSingleton::CreateStream(const std::string& name,const std::string& fullPath) { if (_streamStack.size() > 0) { LogStream* stream = _streamStack.top(); return stream->CreateStream(name,fullPath); } return new LogStreamSimpleFile(name,fullPath); } bool LogManagerSingleton::RegisterLog(const std::string path) { // TODO: Add your specialized code here. std::string opPath = path; if(opPath[0] != '/') return false; std::string fullPath=opPath; std::string childName; std::string tempStr=opPath; LogSwitchBox* parent=_root,*child=0; int offset=0,offsetEnd=0; opPath=opPath.c_str()+1;//去掉'/' for(;;) { offset=static_cast<int>(opPath.find('/')); if(-1 != offset) {//非叶子 childName=""; childName.append(opPath.c_str(),offset); opPath=opPath.c_str()+offset+1;//去掉childName/ child=(LogSwitchBox*)(parent->FindChildByName(childName)); if(!child) { child=new LogSwitchBox(childName); parent->AddChild(child); } parent=child; } else {//叶子 //childName="" childName=opPath; LogCtrl* leaf=parent->FindChildByName(childName); if(!leaf) {//无同名叶子 LogStream* stream=CreateStream(childName,fullPath); leaf=new LogLeaf(childName,stream); parent->AddChild(leaf); return true; } else { LogStream* stream=CreateStream(childName,fullPath); ((LogLeaf*)leaf)->ChangeStream(stream); } return true; } if(opPath.empty()) return true; } } LogManagerSingleton* LogManagerSingleton::getInstance() { // TODO: Add your specialized code here. // NOTE: Requires a correct return value to compile. if(uniqueInstance) { return uniqueInstance; } else { uniqueInstance=new LogManagerSingleton; return uniqueInstance; } } void LogManagerSingleton::Cleanup(void) { if (uniqueInstance) { delete uniqueInstance; uniqueInstance = 0; } } LogManagerSingleton::LogManagerSingleton() : _root(0) { // ToDo: Add your specialized code here and/or call the base class _root=new LogSwitchBox(std::string("/")); } LogManagerSingleton::~LogManagerSingleton() { // ToDo: Add your specialized code here and/or call the base class _root->Flush(); delete _root; _root=0; LogStream* stream = 0; while (!_streamStack.empty()) { stream = _streamStack.top(); stream->ReleaseStream(); _streamStack.pop(); } } LogSwitchBox* LogManagerSingleton::GetRoot() const { return _root; } int LogManagerSingleton::Report(const std::string path,std::string desc) { if(_root) _root->Report(path,desc); return 0; } int LogManagerSingleton::Report(const std::string path, std::string desc,long num) { char sz[20]={""}; _snprintf(sz,20,"%d",num); desc.append(sz); if(_root) _root->Report(path,desc); return 0; } // 设置节点者叶子的状态 void LogManagerSingleton::Enable(const std::string path,bool enable) { std::string opPath = path; std::string tempStr,childName; int offsetStart=0,offsetEnd=0; LogSwitchBox* parent=_root; LogCtrl* child=0; for(;;) { if(opPath.empty()) return; offsetStart=static_cast<int>(opPath.find('/')); tempStr=opPath.c_str()+1;//去掉第一个'/' offsetEnd=static_cast<int>(tempStr.find('/')); if(offsetStart == -1 || -1 == offsetEnd) {//叶子 第2中情况是opPath为"/xxxxx",也就是根的叶子 if(offsetStart == -1) childName=opPath; else childName=opPath.c_str()+1; child=parent->FindChildByName(childName); if(!child) { #ifdef _ENABLE_WITH_NEW LogStream* stream=new LogStreamSimpleFile(childName); child=new LogLeaf(childName,stream); parent->AddChild(child); #else #ifdef _WIN32 MessageBox(0,opPath.c_str(),"LOG PATH NOT FOUND",MB_OK); #endif return; #endif } child->Enable(enable); return; } else {//非叶子 childName=""; childName.append(tempStr.c_str(),offsetEnd); child=parent->FindChildByName(childName); if(!child) { #ifdef _ENABLE_WITH_NEW child=new LogSwitchBox(childName); parent->AddChild(child); #else return; #endif } opPath=tempStr.c_str()+1+offsetEnd; parent=(LogSwitchBox*)child; continue; } } } void LogManagerSingleton::PushStream(LogStream* stream) { _streamStack.push(stream); } LogStream* LogManagerSingleton::PopStream(void) { if (_streamStack.size() > 0) { LogStream* stream = _streamStack.top(); _streamStack.pop(); return stream; } return 0; } void LOG_REPORTVAR(const char* path,const char* pszString,...) { char szDesc[1024*32]; //修改Log长度为1024 va_list va; va_start(va, pszString); #ifdef _WIN32 StringCbVPrintf(szDesc, 1024*32, pszString, va); //修改Log长度为1024 #else wvsprintf( szDesc, pszString, va ); #endif va_end(va); LOG_REPORT(path, szDesc); } void LOG_ASSERT(bool exp,const char* pszString,...) { if (!exp) { char szDesc[256]; //修改Log长度为1024 va_list va; va_start(va, pszString); #ifdef _WIN32 StringCbVPrintf(szDesc, 256, pszString, va); //修改Log长度为1024 #else wvsprintf( szDesc, pszString, va ); #endif va_end(va); //MessageBox(0,szDesc,"Assert",MB_OK); //LOG_REPORT("/Foundation/Assert", szDesc); } } #ifdef _DEBUG void LOG_DEBUG_REPORT(const char* path,const char* pszString,...) { char szDesc[1024*32]; va_list va; va_start( va, pszString ); #ifdef _WIN32 StringCbVPrintf( szDesc,1024*32, pszString, va ); #else wvsprintf( szDesc, pszString, va ); #endif va_end( va ); LOG_REPORT(path,szDesc); } #else void LOG_DEBUG_REPORT(const char* path,const char* pszString,...) { } #endif
11-07
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值