1.Objective-C簡介
1.1 簡介
1.基于C語言,在C語言基礎上,做了面向對象擴展。
2.1980年代初由 Brad Cox 和 Tom Love 發明,后來稱為NeXT的主力語言,后被蘋果收購,成為蘋果開發平臺的主力語言。
3.與Cocoa 和Cocoa Touch框架高度集成,支持開發Mac OS X、iOS應用。
-Mac OS X是蘋果公司為Mac系列產品開發的專屬操作系統。
-iOS是由蘋果公司開發的手持設備操作系統。
4.在蘋果開發平臺上,通過LLVM編譯器架構,支持與Swift語言雙向互操作。
1.2 iOS開發平臺
1.3開發方式
-Clang 或 GCC 命令行
a.clang -fobjc-arc Hello.m
b.-fobjc-arc 支持ARC內存管理
c.適合調試、研究
-Xcode項目
a.構建正規工程項目
b.使用大型框架,追求設計質量與代碼組織
-Demo
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]){
@autoreleasepool {
NSLog(@"Hello, World!");
}
return 0;
}
1>NSLog是一個日志輸出函數,可以將傳入的字符串參數輸出到控制臺。
2>@"Hello,World!"
是OC字符串,前面有@。
3>
#import
,包含某個文件的內容到 處理指令所在位置。#import <Foundation/Foundation.h>
表示包含Foundation框架中的Foundation.h文件。
OC使用
#import
包含頭文件,可以自動防止同一個頭文件被包含多次。
1.4 ObjC編譯過程
2.類與對象
類(class)是一種表示對象類型的結構體。類有屬性和方法。
對象(object)是類的具體化的東西,從抽象中到具體化出的特定個體。
OC一般用2個文件描述一個類(.h和.m)。
1> .h:類聲明文件,用于聲明成員變量、方法。類聲明關鍵字@interface 和 @end。
RPoint.h
@interface RPoint: NSObject @property int x; @property int x; -(void) print; @end
2> .m:類的實現文件,用于實現.h中聲明的方法。類實現關鍵字@implementation和@end。
RPoint.m
#import <Foundation/Foundation.h>
#import "rpoint.h"
@implementation RPoint
-(void) print{
NSLog(@"[%d, %d]", self.x,self.y);
}
對象
創建對象
#import <Foundation/Foundation.h>
#import "rpoint.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
RPoint* rp1=[[RPoint alloc] init];
}
return 0;
}
-->要用到Rpoint這個類,所以包含它的頭文件#import "rpoint.h"
-->(1)調用Rpoint類的靜態方法alloc分配存儲空間,
RPoint* rp1=[[RPoint alloc] init];
返回Rpoint對象,用一個指向Rpoint類型的指針變量rp1來接收這個對象。用類名定義一個變量時,類名后面一定要帶個*號。
alloc方法聲明:+(id)alloc;
,返回值類型時id,id代表任何指針類型,可以代表任何OC對象。
-->(2)調用RPoint對象的構造方法init進行初始化
rp1 = [rp1 init];
類——引用類型
1>位于棧上的指針(引用)
2>位于堆上的實體對象
對象:棧上存儲指針(引用),堆上存儲真正的對象。
結構——值類型
實例直接位于棧中。
棧(stack)
1>無ARC負擔,由系統自動管理,以執行函數為單位。
2>空間大小編譯時確定(參數+局部變量)。
3>函數執行時,系統自動分配一個stack。
4>函數執行結束,系統立即自動回收stack。
5>函數之間通過拷貝值傳遞。
6>具有局部性,大小有限額,超出會stack overflow。
堆(heap)
1>分配由程序員手動請求(創建對象時)。
2>釋放由運行時ARC機制自動釋放(確定時)。
3>函數之間通過拷貝引用(指針)傳遞。
4>具有全局性,總體無大小限制(受制于系統內存整體大小)。
拷貝行為
RPoint* rp2 = rp1;
SPoint sp2 = sp1;
rp2.x++; rp2.y++;
sp2.x++; sp2.y++;
傳參行為
process(rp1,sp1);
void process(RPoint* rp3, SPoint SP3){
rp3.x++;
rp3.y++;
sp3.x++;
sp3.y++;
}
3.數據成員:屬性與實例變量
3.1 屬性
屬性表達實例狀態,描述類型對外接口。相比較直接訪問實例變量,屬性可以做更多控制。
默認情況下,編譯器會為屬性定義propertyName自動合成:
1>一個getter訪問器方法:propertyName
2>一個setter訪問器方法:setPropertyName
3>一個實例變量 _propertyName
可以自定義訪問器方法,也可以更改訪問器方法名、或實例變量名。
可以使用靜態全局變量(C語言)+類方法,模擬類型屬性。
屬性聲明由@property 指令以及緊跟其后的特性組成。
@property NSString* firstName;
@property NSString* lastName;
@property int age;
@property NSDate* birthday;
@property (readonly) NSString* fullName;
3.2 實例變量
可以定義實例變量,而不定義屬性。只有實例變量,沒有類變量。
如果同時自定義了getter和setter訪問器方法,或者針對只讀屬性定義了getter訪問器方法,編譯器將不再合成實例變量。
在類外一律使用屬性來訪問,類內大多也通過self使用屬性訪問。只有以下情況使用實例變量來訪問:
1>初始化器 init
2>析構器 dealloc
3>自定義訪問器方法
3.2 實例變量的生存周期
實例變量的存儲:跟隨對象實例存儲在堆上。
值類型實例變量直接“內嵌”在對象實例中。跟隨對象實例內存釋放而被釋放。
引用類型實例變量通過指針“引用”堆上的引用類型實例,ARC針對引用進行計數管理,自動釋放引用計數為0的對象。
3.3 屬性的描述特性(Attribute)
可以指定屬性不同環境下的不同功能。
->讀寫特性:
readwrite或者readonly
指定該屬性是否可寫。默認是可讀可寫
@property (readonly) NSString* fullName;
->多線程特性:
nonatomic
指定生成的存取器函數是非原子性的,非線程安全;默認atomic
原子性,線程安全。
->內存管理特性
-->ARC環境
強引用strong(默認)
弱引用weak阻止循環引用
拷貝屬性copy創建獨立拷貝
-->其它情況
assign、retain、unsafe_unretained
決定為該屬性生成的賦值函數的類型。
assign 簡單地為變量賦值。retain 賦值到變量時會保留傳入的參數。默認值是 assign
--循環引用(強引用)
WorkItem* workItem=[[WorkItem alloc] init];
workItem.content=@"CRM";
--弱引用
employee.workItem=workItem;
workItem.owner=employee;
--拷貝
a.賦值前
NSLog(@"Work Content: %@",workItem.content);
結果:Work Content: CRM
b.賦值后
NSMutableString *workContent = [NSMutableString stringWithString:@"ERP"];
workItem.content=workContent;
NSLog(@"Work Content: %@",workItem.content);
結果:Work Content: ERP
4.函數成員:方法
方法 Method
函數:代碼段上的可執行指令序列
->全局函數(C語言函數)
->成員函數(ObjC方法)
方法是類的成員函數,表達實例行為或類型行為。
所有方法默認為公有方法。沒有private或protected方法。
動態消息分發:方法調用通過運行時動態消息分發實現,在對象上調用方法又稱“向對象發送消息”。
-(void) print;
-(BOOL) isEqualToPoint: (BLNPoint*) point;
+(BLNPoint*) getOriginPoint;
1.'-'表示動態方法(實例方法);'+'表示靜態方法(類型方法)。
2.print前面的(void)表示方法無返回值,方法的返回值和參數類型都需要用小括號()包住。
3.OC方法中,一個冒號:對應一個參數。冒號 : 也是方法名的一部分。例如:isEqualToPoint:
4.方法調用:
BLNPoint* p1=[[BLNPoint alloc] init];
[p1 print];
實例方法或類型方法
1>實例方法——表達實例行為,可以訪問
-->實例成員(實例屬性、實例變量、實例方法)
-->類型方法、靜態變量
2>類方法——表達類型行為,訪問權限:
-->可以訪問:類型方法、靜態變量
-->不能訪問:實例成員(實例屬性、實例變量、實例方法)
3>了解編譯器背后對實例方法和類方法的不同處理:self指針
對實例方法:實例對象的指針
對類方法:當前類的表示
方法參數
->如果參數類型為值類型,則為傳值方式
-(void) moveToX:(int)x toY:(int)y;
如果參數類型為引用類型,則為傳指針方式
-(BOOL) isEqualToPoint: (BLNPoint*) point;
->方法可以沒有參數,也可以沒有返回值
->如果方法有參數,方法名約定包含第一個參數名,從第二個參數開始需要顯式提供外部參數名。
-(void) moveToX:(int)x toY:(int)y;
moveToX
包含第一個參數名,toY
為第二個參數的外部參數名。
->調用時,第一個參數名忽略,但后面的參數名必須顯式標明。
[p1 moveToX:100 toY:200];
動態方法調用機制——消息分發
BLNPoint* origin=[BLNPoint getOriginPoint];
[origin print];
id obj=[[BLNPoint alloc] init];
[obj moveToX:50 toY:60];
[obj print];
//JMP obj-> methodLists-> &print
5.初始化器與析構器
認識
1.初始化器用于初始化對象實例或者類型,是一個特殊的函數。
->對象初始化器:-(id) init可以重載多個
-(id)init;
-(id)initWithName:(NSString *)name;
-(id)initWithName:(NSString *)name WithPages:(int)pages;
-(id)initWithName:(NSString *)name WithPages:(int)pages WithCategory:(NSString*)category;
->類型初始化器:+(void)initialize只能有一個
+(void)initialize;
2.析構器用于釋放對象擁有的資源,無返回值的函數。
->對象析構器 -(void)dealloc 只有一個
->沒有類型析構器
對象初始化器
->初始化對象實例時,init通常和alloc搭配使用。
->alloc所做的事情——NSObject已實現:
-->1.在堆上分配合適大小的內存。
-->2.將屬性或者實例變量的內存置0。
->init所做的事情——可以自定義:
-->1.調用父類初始化器[super init](前置調用)。
-(id)init{
self = [super init];
if(self){
NSLog(@"Book Object init");
}
return self;
}
-->2.初始化當前對象實例變量(使用實例變量,不使用屬性)。
-(id)initWithName:(NSString *)name WithPages:(int)pages WithCategory:(NSString*)category
{
self = [super init];
if (self) {
NSLog(@"Book Object init");
_name = [name copy];
_pages = pages;
_category = [category copy];
}
return self;
}
->new相當于調用alloc/init的無參數版本。
類初始化器
->類初始化器initialize負責類型級別的初始化。
->initialize在每個類使用之前被系統自動調用,且每個進程周期中,只被調用一次。
->子類的initialize會自動調用父類的initialize(前置調用)。
+(void)initialize
{
if(self ==[Book class]){
NSLog(@"Book Class initialize");
}
}
對象析構器
->對象析構器dealloc負責釋放對象擁有的動態資源:
-->自動實現:1. ARC 將對象屬性引用計數減持
-->手動實現:2.釋放不受ARC管理的動態內存,如malloc分配的內存
-->手動實現:3.關閉非內存資源,如文件句柄、網絡端口
-(void)dealloc
{
//1. 自動調用:ARC 對對象屬性的引用技術減持
//2. 手工實現
NSLog(@"Book Object release");
//3. 自動調用:父類dealloc
}
->dealloc由ARC根據對象引用計數規則,在釋放對象內存前自動調用,無法手工調用。
->子類的dealloc會自動調用父類的dealloc(后置調用)。
-->1.子類的某些對象實例繼承自父類。需要調用父類的dealloc方法,來釋放父類擁有的這些對象。
-->2.調用順序:當子類的對象釋放完時,再釋放父類所擁有的實例。
6.繼承
面向對象特性
->封裝 encapsulation
隱藏對象內部實現細節,對外僅提供公共接口訪問。
->繼承 inheritance
一個類型在另外類型基礎上進行的擴展實現。
->多態 polymorphism
不同類型針對同一行為接口的不同實現方式。
繼承 Inheritance
->繼承:每一個類只能有一個基類,子類自動繼承基類的:
-->實例變量
-->屬性
-->實例方法
-->類方法
Shape.h
#import <objc/runtime.h>
#import <Foundation/Foundation.h>
@interface Shape : NSObject {
@public int _data;
}
@property int no;
-(void)draw;
-(void)move;
-(void)print;
+(void)process;
@end
Circle.h
#import "Shape.h"
#import <Foundation/Foundation.h>
@interface Circle : Shape
@property int radius;
@end
子類 Circle繼承基類Shape
Circle* circle = [[Circle alloc]init];
circle.no=200;//訪問屬性
circle->_data++;//訪問實例變量
[circle draw];//訪問實例方法
[circle print];//訪問實例方法
[Circle process];//訪問類方法
->所有類的根類:NSObject
->繼承的兩層含義:
-->成員復用:子類復用基類成員
*私有成員也被繼承,子類訪問不到。
-->類型抽象:將子類當作父類來使用(IS-A關系準則)
Circle is a Shape.
void process(Shape *shape){
shape.no++;
[shape draw];
}
process(circle);
7.多態
多態 Polymorphism
->多態:子類在父類統一行為接口下,表現不同的實現方式。
->對比重寫與重載
-->子類重寫父類同名同參數方法:子類只可以重寫父類方法。
Rectangle繼承Shape
@interface Rectangle : Shape
Rectangle重寫Shape的方法:
#import "Rectangle.h"
@implementation Rectangle
//override 重寫
-(id)init
{
self = [super init];
if (self) {
_length = 10;
_width = 20;
}
return self;
}
-(void)draw
{
NSLog(@"Rectangle object draw: length=%d, width=%d", self.length,self.width);
}
-(void)print
{
NSLog(@"Rectangle Instance variable %d", _data);
}
+(void)process
{
NSLog(@"Rectangle class process");
}
-(void)dealloc
{
NSLog(@"Rectangle dealloc");
}
@end
使用:
Rectangle *rect = [[Rectangle alloc]init];
rect.no=200;
rect->_data++;
[rect draw];
[rect print];
[Rectangle process];
[rect move];
----Shape是聲明類型,Rectangle實際類型
Shape *rect = [[Rectangle alloc]init];
rect.no=200;
rect->_data++;
[rect draw];
[rect print];
[Rectangle process];
[rect move];
-->方法名相同、參數不同:OC不支持方法的重載。
->在子類的代碼中,可以使用super來調用基類的實現。
-->self具有多態性,可以指向不同子類。
-->super沒有多態性,僅指向當前父類。
Rectangle.m
-(int)no{
return super.no;
}
-(void)setNo:(int)no{
super.no=no;
}
Shape.m
{
[self draw];
}
繼承中的init和dealloc
->初始化器 init
-->子類自動繼承基類的初始化器
-->子類也可以重寫基類初始化器,此時子類初始化器必須首先調用基類的一個初始化器(手工調用)。
->析構器 dealloc
-->子類可以選擇繼承基類析構器,或者重寫基類析構器。
-->子類析構器執行完畢后,會自動調用基類析構器(后置調用,且不支持手工調用)。
-->子類析構器自動具有多態性。
->盡量避免在父類 init 和 dealloc 中調用子類重寫的方法。