面向?qū)ο笳Z言O(shè)C,在語法上使用的是“消息結(jié)構(gòu)”,而非“函數(shù)調(diào)用”
消息結(jié)構(gòu)和函數(shù)調(diào)用之間的區(qū)別//messaging(OC)
object* obj = [object new];
[obj performWith:parameter1 and:parameter2];
//function calling (C++)
object* obj = new object;
obj ->perform(parameter1, parameter2);
關(guān)鍵區(qū)別:使用消息結(jié)構(gòu)的語言,其運行時所應(yīng)執(zhí)行的代碼由運行環(huán)境來決定;而使用函數(shù)調(diào)用的語言則由編譯器來決定。
如果范例代碼中調(diào)用的函數(shù)是多態(tài)的,那么在運行時就要按照“虛方法表”來查出到底應(yīng)該執(zhí)行哪個函數(shù)實現(xiàn)。而采用消息結(jié)構(gòu)的語言,不論是否多態(tài),總是在運行時才會去查找所要執(zhí)行的方法
實際上,編譯器不關(guān)心接受消息的對象是何種勒烯女。接收消息的對象問題也要在運行時候處理,其過程叫做“動態(tài)綁定”OC的重要工作都由“運行期組件”而非編譯器來完成。
使用OC的面向?qū)ο筇匦运璧娜繑?shù)據(jù)結(jié)構(gòu)及函數(shù)都在運行期組件李敏啊。舉例來說,運行期組件中含有全部的管理內(nèi)存的方法。運行期組件本質(zhì)上就是一種與開發(fā)者所編代碼相鏈接的“動態(tài)庫”,其代碼能把開發(fā)者編寫的所有程序粘合起來。
這樣的話,只需要更新運行期組件,即可提升應(yīng)用程序性能。而那種許多工作都需要在“編譯期”完成的語言,若是想要獲得類似的性能提升,則需要重新編譯應(yīng)用程序代碼。
OC是C的“超集”所以c語言中的所有功能在編寫OC代碼的時候依然適用。因此必須同時掌握C與OC這兩門語言的核心概念,才能寫出高效的OC代碼來。
其中尤為重要的是要理解C語言的內(nèi)存模型,這有利于理解OC的內(nèi)存模型及“引用計數(shù)”機制的工作原理。
理解內(nèi)存模型:OC中的指針是用來指示對象的,想要聲明一個變量,令其指代某個對象,可用以下語法NSString* someString = @"THE String";
這種語法基本上是照搬c語言的,他聲明了一個名為someString的變量,類型為NSString*也就是說,此變量為指向NSString的指針,所有OC語言的對象都必須這樣聲明,因為所占的內(nèi)存總是分配在“堆空間”中,而絕不會分配在“棧上”,不能在棧上分配OC對象
someString變量指向分配在堆里的某塊內(nèi)存,其中含有一個NSString對象。也就是說,如果再創(chuàng)建一個變量,令其指向同一地址,那么并不拷貝該對象,只是這兩個變量會同時指向此對象
這說明當前“棧幀”里面分配了兩塊內(nèi)存,每塊內(nèi)存的大小都能容下一枚指針(32位4字節(jié),64位8字節(jié))。兩塊內(nèi)存里面的值是一樣的,就是NSString實例的內(nèi)存地址
分配在堆中的內(nèi)存必須直接管理,而分配在棧上用于保存變量的內(nèi)存則會在其棧幀彈出時自動清理。
OC將堆內(nèi)存管理抽象出來了。不需要用malloc和free來分配或釋放對象所占內(nèi)存。OC運行期環(huán)境把這部分工作抽象為一套內(nèi)存管理架構(gòu),名叫“引用計數(shù)”
在OC代碼中有時會遇到定義不含*的變量,他們可能會使用??臻g,這些變量保存的不是OC對象 CGRect就是例子
CGRect是c結(jié)構(gòu)體
struct CGRect {CGpoint origin;
CGSize siza;
};
typedef struct CGRect CGRect;
整個系統(tǒng)框架都在使用這種結(jié)構(gòu)體,如果改用OC對象來做的話,性能會受影響
,與創(chuàng)建結(jié)構(gòu)體相比,創(chuàng)建對象還需要額外開銷,例如分配釋放堆內(nèi)存等,如果只需要保存 int float double char等“非對象類型”那么通常使用CG熱潮天、這種結(jié)構(gòu)體就行
內(nèi)存模型
與指針virtual method table 是編程語言為實現(xiàn)“動態(tài)派發(fā)”或“運行時方法綁定”而采用的一種機制。
在類頭文件中盡量少引入其他頭文件與C和C++一樣使用頭文件與實現(xiàn)文件來區(qū)隔代碼,用OC語言編寫“類”的標準方式為:以類名做文件名,分別創(chuàng)建兩個文件,頭文件后綴用.h,實現(xiàn)文件后綴用.m,創(chuàng)建好一個類之后,其代碼看上去如下所示
//
// EOCPerson.h
// EOC1.1
//
// Created by zzy on 2022/12/22.
//
#importNS_ASSUME_NONNULL_BEGIN
@interface EOCPerson : NSObject
@property (nonatomic, copy) NSString* firstName;
@property (nonatomic, copy) NSString* lastName;
@end
NS_ASSUME_NONNULL_END
// EOCPerson.m
// EOC1.1
//
// Created by zzy on 2022/12/22.
//
#import "EOCPerson.h"
@implementation EOCPerson
@end
OC語言編寫任何類基本都需要引入Foundation框架,如果不引入這個文件的話,那么就要引入與其超類所屬框架相對應(yīng)的“基本頭文件”,在創(chuàng)建iOS應(yīng)用程序時,通常會繼承UIViewController類,而這些子類的的頭文件需要引入UIKit.h
EOCPerson類其頭文件中引入了整個Foundation框架,那如果此類繼承自Foundation框架中的某個類,那么EOCPerson類的使用者可能會用到其基類的中的許多內(nèi)容。繼承自UIViewController的那些類也是如此,其使用者可能會用到UIKit中的大部分內(nèi)容。
你創(chuàng)建了一個新類EOCEmployer,然后你覺得每個EOCPerson實例都需要有一個EOCEmployer于是添加了一個屬性@property (nonatomic, strong) EOCEmployer *employer;
但你需要引入#import "EOCEmployer.h"
,這種辦法可行,但是不夠優(yōu)雅,在編譯一個使用了EOCPerson類的文件時,不需要知道EOCEmployer的具體細節(jié),只需要知道有一個類名叫EOCEmployer就好,有一個辦法可以將這一情況告訴編譯器
@class EOCEmployer;
“向前聲明”,現(xiàn)在EOCPerson的頭文件就是這樣
// EOCPerson.h
// EOC1.1
//
// Created by zzy on 2022/12/22.
//
#import@class EOCEmployer;
@interface EOCPerson : NSObject
@property (nonatomic, copy) NSString* firstName;
@property (nonatomic, copy) NSString* lastName;
@property (nonatomic, strong) EOCEmployer *employer;
@end
EOCPerson類的實現(xiàn)文件則需要引入EOCEmployer類的頭文件,因為若要使用后者,則需要知道所有接口細節(jié),實現(xiàn)文件就需要引入#import "EOCEmployer.h"
,將引入頭文件的時機盡量延后,只有在確有需要時才引入,以減少類的使用者所引入的頭文件數(shù)量,假設(shè)本例把EOCEmployer.h引入到EOCPerson.h,那么只需要引入EOCPerson.h就可以一并引入EOCEmployer的所有內(nèi)容了,但這樣需要引入許多根本用不到的內(nèi)容,增加了不必要的編譯時間。
在使用oc時要經(jīng)常用到Foundation框架,經(jīng)常用到NSString,NSNumber,NSArray,NSDictionary。從類名上即可看出各自所表示的數(shù)據(jù)結(jié)構(gòu)。
有一種方式能非常簡單的方式能創(chuàng)建NSString對象:“字符串字面量”,NSString *someString = @"EOC2.0";
如果不用這種語法的話就要用常見的alloc init方法來分配并初始化NSString對象了,這種方法可以縮減源代碼長度,使其更為易讀。
能夠以NSNumber實例表示的所有 數(shù)據(jù)類型都可以使用該語法。
NSString *someString = @"EOC2.0";
NSNumber *intNumber = @1;
NSNumber *floatNumber = @2.5f;
NSNumber *doubleNumber = @3.14159;
NSNumber *boolNumber = @YES;
NSNumber *charNumber = @'a';
//字面量語法也適用于以下表達式;
int x = 5;
float y = 6.32f;
NSNumber *expressionNumber = @(x * y);
字面量數(shù)組數(shù)組是常用的數(shù)據(jù)結(jié)構(gòu),非字面量語法創(chuàng)建實例:
NSArray *season = [NSString arrayWithOjbects:@"spring",@"summer",@"autumn",@"winter",nil];
使用字面量語法:
NSArray *season = @[@"spring",@"summer",@"autumn",@"winter"];
這種做法不僅簡單,而且利于操作數(shù)組。數(shù)組常見操作就是取某個下標所對應(yīng)的對象,如果不用字面量操作:
NSString *sum = [season objectAtIndex:1];
使用字面量:
NSString *sum = season[1];
要注意,若數(shù)組元素對象中有nil,則會拋出異常,字面量語法實際上只是一種語法糖,其效果等于是先創(chuàng)建了一個數(shù)組,然后把方括號內(nèi)的所有對象都添加進數(shù)組,如果發(fā)現(xiàn)nil則會停止添加(arrayWithObjects:方法會依次處理各個參數(shù),直到發(fā)現(xiàn)nil為止)
使用字面量語法會更為安全,出現(xiàn)異常會令程序終止執(zhí)行,這比創(chuàng)建好數(shù)組之后才發(fā)現(xiàn)元素個數(shù)少了要好。
Dictionary ,映射型數(shù)據(jù)結(jié)構(gòu),鍵值對
NSDictionary *personData = [NSDictionarydictionaryWithObjectsAndKeys:
@"Matt", @"firstName",
@"Galloway", @"lastName",
[NSNumber numberWithInt:28], @"age",
nil];
順序是 《對象》,《鍵》 與
通常理解為 把“鍵”映射到“對象”,相反
字面量:
NSDictionary *personData = @{@"firstName" : @"Matt",
@"lastName" : @"Galloway",
@"age" : @28};
28區(qū)別原因:字典中的對象和鍵必須都是Objective-C對象,所以不能把28直接放進去,而要封裝在NSNumber實例中才行
由鍵訪問其值 : 不用字面量: NSString *lastNamge = [personData objectForKey:@“l(fā)astName”];
使用字面量:NSString *lastName = personData[@“l(fā)astName”];
可變數(shù)組與字典:修改可變數(shù)組與字典內(nèi)容的標準做法是:
[mutableArray replaceObjectAtIndex:1 withObject:@"dog"];
[mutableDictionary setObject:@"Galloway"forKey:@"lastName"];
換成去下標:
mutableArray[1] = @"dog";
mutableDictionary[@"lastName"] = @"Galloway";
局限性:除了字符串以外,所創(chuàng)建出來的對象必須屬于Foundation框架。
使用字面量語法創(chuàng)建出來的字符串、數(shù)組、字典對象都是不可變的(immutable)
想要可變版本的對象,需復制一份:NSMutableArray *mutable = [@[@1, @2, @3]mutableCopy];
你是否還在尋找穩(wěn)定的海外服務(wù)器提供商?創(chuàng)新互聯(lián)www.cdcxhl.cn海外機房具備T級流量清洗系統(tǒng)配攻擊溯源,準確流量調(diào)度確保服務(wù)器高可用性,企業(yè)級服務(wù)器適合批量采購,新人活動首月15元起,快前往官網(wǎng)查看詳情吧