1. 程式人生 > 實用技巧 >OC基礎--資料型別與表示式

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、CGFloatid、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));