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輸出格式如下表所示。

格式字符 說明 格式字符 說明

帶符號十進制 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

Objective-C裏核心的處理字符串的類就是NSString和NSMutableString這兩個類,這兩個類完成了Objective-C中字符串大部分功能的處理。字符串內容比較多,我們後面單獨寫一篇,到時候鏈接補上來。

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

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章