iOS學習——#define、const、typedef的區別

来源:https://www.cnblogs.com/mukekeheart/archive/2019/02/21/10412979.html
-Advertisement-
Play Games

在iOS開發中經常遇到一些欄位和類型的定義,例如配置生產和測試不同環境的參數等,這時候經常用到#define、const以及typedef。那麼它們之間有什麼區別呢?我們接下來一個一個具體瞭解下。 一、基本概念 1.1、#define #define並不是定義全局變數,而是巨集定義。也就是說並不是真正 ...


在iOS開發中經常遇到一些欄位和類型的定義,例如配置生產和測試不同環境的參數等,這時候經常用到#define、const以及typedef。那麼它們之間有什麼區別呢?我們接下來一個一個具體瞭解下。

一、基本概念

1.1、#define

  #define並不是定義全局變數,而是巨集定義。也就是說並不是真正意義上的定義變數,而是用來做文本替換。當程式開始運行時,編譯器會先將代碼中的MAX全部替換為100,然後再進行編譯。由此可得,#define並不是在編譯過程中進行,而是在預編譯階段進行。

#define MAX 100 

  巨集的常見用法:

  • 常見的字元串抽成巨集:比喻定義的常用顏色、字體字型大小等
    #define kWaterAlpha 0.04f  //水印的透明度
    
    #define kFlowRowSize 30  //流程每次拉去的數量
    
    #define ROW_SIZE 20  //一般刷新每次拉去的數量
  • 常見代碼抽成巨集:比喻單例模板等
    //巨集定義常用的顏色
    #define XRGB(r,g,b)     [UIColor colorWithRed:(0x##r)/255.0 green:(0x##g)/255.0 blue:(0x##b)/255.0 alpha:1]
    #define kBgColor XRGB(f4, f4, f4)
    #define kBlackFontColor XRGB(33, 33, 33)
    #define kGrayFontColor XRGB(99, 99, 99)
    #define kBlueFontColor XRGB(3d,9a,e8)
    
    //巨集定義獲取當前主界面rootViewController
    #define RootVC [UIApplication sharedApplication].delegate.window.rootViewController
    
    //巨集定義獲取當前的界面
    #define TopVC ([RootVC isKindOfClass:[UITabBarController class]]?[((UITabBarController *)RootVC).selectedViewController topViewController]:RootVC)
    
    //巨集定義單例的定義和實現
    #define DECLARE_DEFAULT  +(instancetype)defaultInstance;
    
    #define IMPLEMENT_DEFAULT(C)  +(instancetype)defaultInstance{\
    static dispatch_once_t onceToken;\
    static C *_gInstance;\
    dispatch_once(&onceToken, ^{\
    _gInstance = [C new];\
    });\
    return _gInstance;\
    }

  關於#define的其他用法可以參見後面這篇博文:iOS開發中你真的會用#define麽!!!?

1.2、const

  關鍵字const用來定義常量,如果一個常量被const修飾,那麼他的值就不能被改變。編譯器通常不為普通const常量分配存儲空間,而是保存於符號表中,這使得它成為一個編譯期間的常量,沒有存儲與讀記憶體的操作,使得它的效率更高。

  常見用法如下:

//全局變數,constString1地址不能修改,constString1值能修改
const NSString *constString1 = @"I am a const NSString * string";
//意義同上,無區別
NSString const *constString2 = @"I am a NSString const * string";
// stringConst 地址能修改,stringConst值不能修改
NSString * const stringConst = @"I am a NSString * const string";
  • constString1 跟constString2 無區別
  • 左邊代表指針本身的類型信息,const表示這個指針指向的這個地址是不可變的
  • 右邊代表指針指向變數的可變性,即指針存儲的地址指向的記憶體單元所存儲的變數的可變性

1.3、typedef

  typedef常用於給類型起別名(給已知的類型起別名)。常用於簡化複雜類型,變數類型意義化等。typedef是類型替換,語句的一種,結尾必須有;

//iOS底層源碼就是對NSInteger進行了一個別名的設置,其表示的就是long或者int類型。
#if __LP64__ || (TARGET_OS_EMBEDDED && !TARGET_OS_IPHONE) || TARGET_OS_WIN32 || NS_BUILD_32_LIKE_64
    typedef long NSInteger;
    typedef unsigned long NSUInteger;
#else
    typedef int NSInteger;
    typedef unsigned int NSUInteger;
#endif

  

  在iOS開發中最常用到的應該就是使用typedef定義枚舉和block了,此外還可以用typedef定義函數。關於typedef定義枚舉官方API是這樣說的

/* NS_ENUM supports the use of one or two arguments. The first argument is always the integer type used for the values of the enum. The second argument is an optional type name for the macro. When specifying a type name, you must precede the macro with 'typedef' like so:
NS_ENUM支持單個或兩個參數,第一個參數一般是NSInteger類型來制定枚舉的值類型,第二個參數是可選的枚舉類型的別名,如果要定義別名,必須使用typedef進行定義,具體格式如下:
 
typedef NS_ENUM(NSInteger, NSComparisonResult) {
    ...
};
 
If you do not specify a type name, do not use 'typedef'. For example:
如果不需要使用特定的名稱,則不需要使用typedef
 
NS_ENUM(NSInteger) {
    ...
};
*/

  

  typedef的常見用法如下:

typedef double NSTimeInterval;  //給double取別名為NSTimeInterval(變數類型意義化)
typedef NSTimeInterval MyTime;  //給NSTimeInterval取別名為MyTime
typedef char * MyString;  //給char *取別名為MyString

//c語言格式,給Person結構體取別名為MyPerson。使用:MyPerson p = {"jack"};
typedef struct Person {
    char *name 
}MyPerson; 

//c語言格式,給Gender枚舉取別名為MyGender。使用:MyGender g = Man;
typedef enum Gender {
    Man,
    Woman 
}MyGender; 

//OC語言格式,給Gender枚舉取別名為MyGender。使用:MyGender g = Man;
typedef NS_ENUM(NSInteger, Gender) {
    Man,
    Woman
};

//給block取別名MyBlock
typedef void(^MyBlock) (int a,int b); 
 
//給指向函數的指針取別名MyFunction
typedef int(*MyFunction) (int a,int b); 

 

  typedef定義函數的示例:

int add (int a, int b){
     return a + b;
}

typedef int(*MyMethod) (int a,int b);

int main(){
    MyMethod m = add;
    m(5,6);   //調用函數
    return 0;
}

 

二、區別

2.1 #define與const

  • 巨集在預編譯時處理(巨集在編譯開始之前就會被替換);而const會在編譯時被處理
  • #define巨集沒有類型,巨集不做任何類型檢查,不會報編譯錯誤,只是替換;而const常量有具體的類型,會編譯檢查,會報編譯錯誤
  • 巨集能定義一些函數,方法;const不能
  • 使用大量巨集,容易造成編譯時間久,每次都需要重新替換
  • 巨集僅僅是展開,有多少地方使用,就展開多少次,不會分配記憶體。(巨集定義不分配記憶體,變數定義分配記憶體。);而const常量會在記憶體中分配(可以是堆中也可以是棧中),const 可以節省空間,避免不必要的記憶體分配
巨集const
#define PI 3.14159 //常量巨集 const doulbe Pi=3.14159; //此時並未將Pi放入ROM中
double I=PI; //預編譯期間進行巨集替換,分配記憶體 double i=Pi; //此時為Pi分配記憶體,以後不再分配!
double J=PI; //再進行巨集替換,又一次分配記憶體 double j=Pi; //沒有記憶體分配
  • const定義常量從彙編的角度來看,只是給出了對應的記憶體地址,而不是象#define一樣給出的是立即數,所以,const定義的常量在程式運行過程中只有一份拷貝(因為是全局的只讀變數,存在靜態區),而 #define定義的常量在記憶體中有若幹個拷貝。
  • 編譯器通常不為普通const常量分配存儲空間,而是將它們保存在符號表中,這使得它成為一個編譯期間的常量,沒有了存儲與讀記憶體的操作,使得它的效率也很高。

2.2 typedef和#define

  • define是文本替換,屬於預編譯指令,本身不參與編譯,除非希望替換的文本中有;否則不用加。
    typedef是類型替換,語句的一種,結尾必須有;
  • define寫在方法/函數中則作用域從寫的地方開始有效,直至使用#undef(不寫此指令則後面一直有效)。typedef寫在方法/函數中則作用域 只在此方法/函數中有效。
  • 若使用 typedef char * MyString; 則 MyString s1,s2等價於 char *s1; char *s2;
    若使用  #define MyString char * 則 MyString s1,s2等價於 char *s1,s2即 char *s1; char s2
    再次說明瞭typedef是類型替換,直接參与編譯,而define只是簡單的文本替換。

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 在視窗1開通一個名為 redis 的通道: 從視窗2傳入信息: 此時視窗1會收到這條信息: 以上, 就是通過 SUBSCRIBE 和 PUBLISH 實現了一個簡單的消息傳遞的過程. 目前我們是有一個視窗開通 redis 通道, 另一個視窗向這個通道傳遞消息, 大家可以試下再多開一個視窗, 也開通 ...
  • ordered set 是根據 score值有序排列的數據集合. 首先還是清空數據, 並清屏, 此步驟省略~~~~ 新建一條 ordered set 數據 myset1, 並存入4個字元串, score 的排列順序為1-4: 查看這個數據: 給 myset1 的值里新加一個字元e, score=10 ...
  • INSERT INTO table SELECT * FROM OPENDATASOURCE ('SQLOLEDB', 'Data Source=172.168.44.146;User ID=sa;password=123' ).tableexec sp_configure 'show advanc ...
  • unordered collection of unique strings.set值是唯一的字元串的無序集合, 把握住兩個特點: 唯一, 無序. 清空所有的數據, 並清理顯示界面: 保存一條 set 數據, 鍵是 myset1, 值是 1, 2, 3, 4 四個數字: 查看鍵myset1 的值: ...
  • select * from dba_jobs ;select * from dba_scheduler_job_run_details t; >這個語句通過制定job名,來查看job的運行的詳細信息 select * from dba_scheduler_jobs; >這個語句可以查看所有job,及 ...
  • 正如前面所講的, redis 的數據結構就是一系列的鍵值對鍵 -> printable ASCII (可列印的 ASCII 碼, 最大值是 512MB)值 -> Primitives (基本的) string 字元串 (最大值是 512MB) Containers (of string) (以其他形 ...
  • 屏幕尺寸 #define kScreenWidth [UIScreen mainScreen].bounds.size.width #define kScreenHeight [UIScreen mainScreen].bounds.size.height 手機型號 #define kISiPhon ...
  • android 動畫分為兩類,View Animation(視圖動畫)和property Animation(屬性動畫),View Animation(視圖動畫)包含了Tween Animation和Frame Animation, property Animation包含Value Animati ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...