OC基礎--資料型別與表示式
前言
做iOS開發有3年了,從當初的小白到現在,斷斷續續看過很多資料,之前也寫過一些博文來記錄,但是感覺知識點都比較凌亂。所以最近準備抽時間把iOS開發的相關知識進行一個梳理,主要分為OC基礎、UI控制元件、多執行緒、動畫、網路、資料持久化、自動佈局、第三方框架等幾個模組進行梳理。本系列的所有博文集合參見:iOS開發知識梳理博文集。本文主要介紹OC基礎--資料型別與表示式。
一 資料型別
Objective-C是在C語言基礎上拓展出的新語言,所以它是完全相容C語言程式碼的,C語言中的基本資料型別如int、float、double和char在Objective-C中是完全可以正常使用的。除此之外,Objective-C還拓展了一些新的資料型別如BOOL、id、instancetype等。
1.1 基本資料型別
因為Objective-C是在C語言基礎上拓展出的新語言,所以它是完全相容C語言程式碼的,C語言中的基本資料型別都可以正常使用,直接來自C語言中的資料型別如下所示。當然,這些資料型別我們在實際開發過程中很少用到(列舉型別有時候會用到)。
#import <Foundation/Foundation.h> int main(int argc, const char * argv[]) { @autoreleasepool { char ch = 'a'; //字元型 short sh = 4; //短整型 int i8 = 015; //整型 八進位制 int i10 = 15; //整型 十進位制 int i16 = 0x15; //整型 十六進位制 //輸出結果: i8 = 13, i10 = 15 i16 = 21 NSLog(@"i8 = %d, i10 = %d i16 = %d", i8, i10, i16); long l = 6l; //長整型 float f = 3.4f; //單精度浮點型 double d = 5.6; //雙精度浮點型//列舉型別
enum EnumDemo { Spring, //預設為0,後面依次自增 Summer, Autumn = 3, //可以指定整數,後面的在這個基礎上自增 Winter }; //結構體是一種集合,它裡面包含了多個變數或陣列,它們的型別可以相同,也可以不同,每個這樣的變數或陣列都稱為結構體的成員 //結構體佔用的記憶體大於等於所有成員佔用的記憶體的總和(成員之間可能會存在縫隙) struct StructDemo { NSString *name; int length; }; //共用體佔用的記憶體等於最長的成員佔用的記憶體。共用體使用了記憶體覆蓋技術,共用體的所有成員佔用同一段記憶體,同一時刻只能儲存一個成員的值,修改一個成員會影響其餘所有成員。 union UnionDemo { int n; char ch; double f; }; } return 0; }
1.1.1 不同資料型別的佔用儲存空間
不同的資料型別佔用的儲存空間不同,同一資料型別在不同編譯器環境下佔用的儲存空間也不一樣。各資料型別佔用的儲存空間如下表所示。
資料型別 | 16位編譯器 | 32位編譯器 | 64位編譯器 |
---|---|---|---|
char | 1byte | 1byte | 1byte |
int | 2byte | 4byte | 4byte |
float | 4byte | 4byte | 4byte |
double | 8byte | 8byte | 8byte |
short int | 2byte | 2byte | 2byte |
unsigned int | 2byte | 4byte | 4byte |
long | 4byte | 4byte | 8byte |
unsigned long | 4byte | 4byte | 8byte |
long long | 8byte | 8byte | 8byte |
1.1.2 不同資料型別的輸出格式
不同資料型別在列印時,我們需要按照指定的格式進行輸出,在OC中NSLog輸出格式如下表所示。
格式字元 | 說明 | 格式字元 | 說明 |
d |
帶符號十進位制 | f | 小數形式輸出,預設輸出6位小數 |
o | 無符號八進位制 | e | 指數形式輸出,數值不分預設輸出6位小數 |
x | 無符號十六進位制 | g | 自動選用%f或%e輸出,保證以最簡形式輸出,並不會輸出無意義的0 |
u | 無符號十進位制 | p | 以十六盡職形式輸出指標變數所代表的地址值 |
s | 輸出C風格字串 | l | 用在d、o、x、u之前用於輸出長整型;在f、e、g之前用於輸出長浮點型 |
m | 用於制定輸出資料所佔的最小寬度為m位 | .n | 對於浮點數,表示輸出n位小數,對於字串,表示擷取的字元個數 |
_ | 表述輸出的數值向左邊對齊 |
1.2 OC封裝的資料型別
除了上面的基本資料型別之外,Objective-C還拓展了一些新的資料型別如BOOL、NSInteger、NSString、CGFloat、id、instancetype等。此外,還有NSNumber、NSValue、NSData等封裝型別,有NSDictionary、NSArray、NSSet等集合資料型別,有CGRect/NSRect、CGPoint/NSPoint、CGSize/NSSize等尺寸相關的 ,還有NSRange、NSIndex等範圍相關。
1.2.1 BOOL/Boolean
Objective-C中的BOOL型別在不同的架構系統上是不一樣的,所以在64-bit架構系統下BOOL是對應C語言中的bool,值只能是1(YES)和0(NO),32-bit架構下是無符號字元型。下面是OC中對BOOL的定義:
#if defined(__OBJC_BOOL_IS_BOOL) // Honor __OBJC_BOOL_IS_BOOL when available. # if __OBJC_BOOL_IS_BOOL # define OBJC_BOOL_IS_BOOL 1 # else # define OBJC_BOOL_IS_BOOL 0 # endif #else // __OBJC_BOOL_IS_BOOL not set. # if TARGET_OS_OSX || TARGET_OS_MACCATALYST || ((TARGET_OS_IOS || 0) && !__LP64__ && !__ARM_ARCH_7K) //非64-bit架構或非__ARM_ARCH_7K # define OBJC_BOOL_IS_BOOL 0 # else //64-bit架構並且__ARM_ARCH_7K # define OBJC_BOOL_IS_BOOL 1 # endif #endif //所以在64-bit架構系統下BOOL是對應C語言中的bool,否則是無符號字元型 #if OBJC_BOOL_IS_BOOL typedef bool BOOL; #else # define OBJC_BOOL_IS_CHAR 1 typedef signed char BOOL; // BOOL is explicitly signed so @encode(BOOL) == "c" rather than "C" // even if -funsigned-char is used. #endif #define OBJC_BOOL_DEFINED #if __has_feature(objc_bool) #define YES __objc_yes #define NO __objc_no #else #define YES ((BOOL)1) #define NO ((BOOL)0) #endif
//用iPhone5和iPhone8模擬器做個實驗 BOOL isOK = 23; NSLog(@"%d", isOK); // iPhone5的列印結果 23 // iPhone8的列印結果 1
Objective-C中的Boolean型別其實就是一個無符號字元型。
/******************************************************************************** Boolean types and values Boolean Mac OS historic type, sizeof(Boolean)==1 bool Defined in stdbool.h, ISO C/C++ standard type false Now defined in stdbool.h true Now defined in stdbool.h *********************************************************************************/ typedef unsigned char Boolean;
1.2.2 NSInteger
OC中的NSInteger就是對整型的一個封裝,64-bit系統上NSInteger對應的是長整形,32-bit系統上對應的是整型。具體我們可以看OC中的原始碼的定義:
//64-bit系統上NSInteger對應的是長整形,32-bit系統上對應的是整型 #if __LP64__ || 0 || NS_BUILD_32_LIKE_64 typedef long NSInteger; typedef unsigned long NSUInteger; #else typedef int NSInteger; typedef unsigned int NSUInteger; #endif
1.2.3 數值封裝型別NSNumber、NSValue、NSData
我們在編碼中,很多時候需要將C裡面原生的資料 (通常是一些結構體) 封裝成物件,這樣可以用NSDictionary或者NSArray來存取訪問。尤其是一些做適配的情況下,這種封裝是不可避免的。Objective-C提供了不少類可以幫助我們,比較常見的是NSNumber,NSValue和NSData。
NSValue主要就是將這些原生的資料封裝成物件,方便我們進行儲存訪問。NSValue主要用來封裝自定義的資料結構,可以是系統框架提供的CGRect/CGPoint/CGSize等資料結構,也可以是自己定義的struct。
//封裝 + (NSValue *)valueWithBytes:(const void *)value objCType:(const char *)type; //解封 - (void)getValue:(void *)value;
//此外還提供了對基本的尺寸範圍相關的封裝和解封 + (NSValue *)valueWithPoint:(NSPoint)point; + (NSValue *)valueWithSize:(NSSize)size; + (NSValue *)valueWithRect:(NSRect)rect; + (NSValue *)valueWithEdgeInsets:(NSEdgeInsets)insets API_AVAILABLE(macos(10.10), ios(8.0), watchos(2.0), tvos(9.0)); @property (readonly) NSPoint pointValue; @property (readonly) NSSize sizeValue; @property (readonly) NSRect rectValue; @property (readonly) NSEdgeInsets edgeInsetsValue API_AVAILABLE(macos(10.10), ios(8.0), watchos(2.0), tvos(9.0));
使用NSValue對NSPoint進行封裝、解封的示例程式碼如下。
NSPoint point = NSPointFromCGPoint(CGPointMake(0, 0)); // NSDictionary *dic = @{@"point" : point}; //報錯,字典中必須是物件 //通過轉化為NSValue進行儲存 NSValue *value = [NSValue valueWithPoint:point]; NSDictionary *dic = @{@"point" : value}; //從字典中獲取NSValue,並從該物件中獲取對應的NSPoint值 NSValue *vv = dic[@"point"]; NSPoint pp = [vv pointValue]; NSLog(@"%@", NSStringFromPoint(pp));
使用NSValue對自定義結構體進行封裝和解封的示例程式碼如下。
//結構體定義 typedef struct StructDemo { NSString *name; int age; } StructDemoTag; //資料建立 StructDemoTag stDemo = {@"zhangsan", 12}; //封裝 NSValue *vv2 = [NSValue value:&stDemo withObjCType:@encode(StructDemoTag)]; NSDictionary *dic2 = @{@"struct_demo" : vv2}; //解封 NSValue *vv3 = dic2[@"struct_demo"]; StructDemoTag stDemo2 = {}; [vv3 getValue:&stDemo2]; NSLog(@"name: %@, age: %d", stDemo2.name, stDemo2.age); //name: zhangsan, age: 12
NSNumber繼承自NSValue,主要是用來封裝ANSI C內建的資料,比如char,float,int等等。這個類提供了一些封裝/解封的方法,這個使用方法很簡單,就不展示了。
//封裝方法 + (NSNumber *)numberWithChar:(char)value; + (NSNumber *)numberWithUnsignedChar:(unsigned char)value; + (NSNumber *)numberWithShort:(short)value; + (NSNumber *)numberWithUnsignedShort:(unsigned short)value; + (NSNumber *)numberWithInt:(int)value; + (NSNumber *)numberWithUnsignedInt:(unsigned int)value; + (NSNumber *)numberWithLong:(long)value; + (NSNumber *)numberWithUnsignedLong:(unsigned long)value; //解封方法 - (char)charValue; - (unsigned char)unsignedCharValue; - (short)shortValue; - (unsigned short)unsignedShortValue; - (int)intValue; - (unsigned int)unsignedIntValue; - (long)longValue; - (unsigned long)unsignedLongValue;
NSData主要是提供一塊原始資料的封裝,將一些圖片、檔案、字串等資料轉化為位元組流資料,方便資料的封裝和流動,比較常見的是NSString/NSImage以及檔案資料的封裝與傳遞。在應用中,最常用於訪問儲存在檔案中或者網路資源中的資料。一般解封方法在圖片UIImage、字串NSString中有對應的從NSData資料建立。
//以下類方法全部都有成員方法的實現和介面,這裡不一一展示 //直接從data封裝 + (instancetype)dataWithData:(NSData *)data; //指定長度的封裝 + (instancetype)dataWithBytes:(nullable const void *)bytes length:(NSUInteger)length; //封裝檔案對應的 + (nullable instancetype)dataWithContentsOfFile:(NSString *)path; + (nullable instancetype)dataWithContentsOfFile:(NSString *)path options:(NSDataReadingOptions)readOptionsMask error:(NSError **)errorPtr; //封裝url對應的 + (nullable instancetype)dataWithContentsOfURL:(NSURL *)url; + (nullable instancetype)dataWithContentsOfURL:(NSURL *)url options:(NSDataReadingOptions)readOptionsMask error:(NSError **)errorPtr;
NSData在字串中的使用示例程式碼如下:
NSString *str = @"hello object-c"; //封裝 NSData *data = [NSData dataWithBytes:[str UTF8String] length:str.length]; //解封 NSString *str2 = [NSString stringWithUTF8String:[data bytes]]; NSLog(@"str:%@", str2);
1.2.4 字串NSString/NSMutableString
字串內容比較多,我們後面單獨寫一篇,到時候連結補上來。
1.2.5 集合資料型別
OC中的集合框架主要就是陣列(NSArray /NSMutableArray、字典(NSDictionry / NSMutableDictionry)、集合(NSSet / NSMutableSet)。這一部分內容也比較多,我們後面也會單獨補充一篇,到時候連結補上來。
1.2.6 尺寸、範圍相關的型別
Object-C中有CGRect/NSRect、CGPoint/NSPoint、CGSize/NSSize等尺寸相關的,其實CG開頭的和NS開頭的都是一個東西,都是struct定義的尺寸相關的結構體,只是定義在不同的框架中。
/* Points. */ struct CGPoint { CGFloat x; CGFloat y; }; typedef struct CG_BOXABLE CGPoint CGPoint; //NSPoint就是CGPoint typedef CGPoint NSPoint; /* Sizes. */ struct CGSize { CGFloat width; CGFloat height; }; typedef struct CG_BOXABLE CGSize CGSize; //NSSize就是CGSize typedef CGSize NSSize; /* Rectangles. */ struct CGRect { CGPoint origin; CGSize size; }; typedef struct CG_BOXABLE CGRect CGRect; //NSRect就是CGRect typedef CGRect NSRect;
CGRect rect = CGRectMake(0, 0, 100, 200); NSRect rect2 = NSRectFromCGRect(rect); CGPoint point = CGPointMake(2, 3); NSPoint point2 = NSPointFromCGPoint(point); CGSize size = CGSizeMake(100, 50); NSSize size2 = NSSizeFromCGSize(size); NSRange range = NSMakeRange(0, 2);
各種尺寸相關的結構體型別資料在OC中列印該如何列印呢?我們需要將結構體型別轉為字串進行列印,系統提供了相應的方法。示例程式碼如下。
CGRect rect = CGRectMake(0, 0, 100, 200); NSRect rect2 = NSRectFromCGRect(rect); NSLog(@"%@", NSStringFromRect(rect)); CGPoint point = CGPointMake(2, 3); NSPoint point2 = NSPointFromCGPoint(point); NSLog(@"%@", NSStringFromPoint(point)); CGSize size = CGSizeMake(100, 50); NSSize size2 = NSSizeFromCGSize(size); NSLog(@"%@", NSStringFromSize(size)); NSRange range = NSMakeRange(0, 2); NSLog(@"%@", NSStringFromRange(range));