];
p->_size = 3.5;
p->_color = 0;
p->_model = 4;
p->_cpu = 1;
####類
創(chuàng)建對(duì)象的時(shí)候返回的地址其實(shí)就是對(duì)象的第0個(gè)屬性的地址
但是需要注意的是: 對(duì)象的第0個(gè)屬性并不是我們編寫的屬性, 而是一個(gè)叫做isa的屬性
isa是一個(gè)指針, 占8個(gè)字節(jié)
其實(shí)類也是一個(gè)對(duì)象, 也就意味著Person也是一個(gè)對(duì)象
平時(shí)我們所說(shuō)的創(chuàng)建對(duì)象其實(shí)就是通過(guò)一個(gè) 類對(duì)象 來(lái)創(chuàng)建一個(gè) 新的對(duì)象
類對(duì)象是系統(tǒng)自動(dòng)幫我們創(chuàng)建的, 里面保存了當(dāng)前對(duì)象的所有方法
而實(shí)例對(duì)象是程序自己手動(dòng)通過(guò)new來(lái)創(chuàng)建的, 而實(shí)例對(duì)象中有一個(gè)isa指針就指向了創(chuàng)建它的那個(gè)類對(duì)象
如下圖:
![類對(duì)象](http://upload-images.jianshu.io/upload_images/2264508-a9485608a39cffd8.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
#####成員變量脓豪,局部變量扫夜,全局變量
寫在類聲明的大括號(hào)中的變量, 我們稱之為 成員變量(屬性, 實(shí)例變量)
成員變量只能通過(guò)對(duì)象來(lái)訪問(wèn)
注意: 成員變量不能離開(kāi)類, 離開(kāi)類之后就不是成員變量
成員變量不能在定義的同時(shí)進(jìn)行初始化
存儲(chǔ): 堆(當(dāng)前對(duì)象對(duì)應(yīng)的堆的存儲(chǔ)空間中)
存儲(chǔ)在堆中的數(shù)據(jù), 不會(huì)被自動(dòng)釋放, 只能程序員手動(dòng)釋放
寫在函數(shù)和大括號(hào)外部的變量, 我們稱之為全局變量
作用域: 從定義的那一行開(kāi)始, 一直到文件末尾
局部變量可以先定義在初始化, 也可以定義的同時(shí)初始化
存儲(chǔ): 靜態(tài)區(qū)
程序一啟動(dòng)就會(huì)分配存儲(chǔ)空間, 直到程序結(jié)束才會(huì)釋放
寫在函數(shù)或者代碼塊中的變量, 我們稱之為局部變量
作用域: 從定義的那一行開(kāi)始, 一直到遇到大括號(hào)或者return
局部變量可以先定義再初始化, 也可以定義的同時(shí)初始化
存儲(chǔ) : 棧
存儲(chǔ)在棧中的數(shù)據(jù)有一個(gè)特點(diǎn), 系統(tǒng)會(huì)自動(dòng)給我們釋放
#####點(diǎn)語(yǔ)法
如果給屬性提供了getter和setter方法, 那么訪問(wèn)屬性就又多了一種訪問(wèn)方式 , 點(diǎn)語(yǔ)法
點(diǎn)語(yǔ)法其實(shí)它的本質(zhì)是調(diào)用了我們的setter和getter方法
點(diǎn)語(yǔ)法是一個(gè)編譯器的特性, 會(huì)在程序翻譯成二進(jìn)制的時(shí)候?qū)?語(yǔ)法自動(dòng)轉(zhuǎn)換為setter和getter方法
如果點(diǎn)語(yǔ)法在=號(hào)的左邊, 那么編譯器會(huì)自動(dòng)轉(zhuǎn)換為setter方法
如果點(diǎn)語(yǔ)法在=號(hào)的右邊, 或者沒(méi)有等號(hào), 那么編譯器就會(huì)自動(dòng)轉(zhuǎn)換為getter方法
點(diǎn)語(yǔ)法的注意點(diǎn):
點(diǎn)語(yǔ)法一般用于給成員變量賦值, 如果不是給成員
變量賦值一般情況下不建議使用, 但是也可以使用
如調(diào)用test方法历谍,可以寫成p.test望侈,相當(dāng)于[p test]
#####多態(tài)
多態(tài)是事物的多種表現(xiàn)形態(tài)
在程序中如何表現(xiàn):
父類指針指向子類對(duì)象
優(yōu)點(diǎn):
提高了代碼的擴(kuò)展性
注意點(diǎn):
如果父類指針指向子類對(duì)象, 如果需要調(diào)用子類特有的方法, 必須先強(qiáng)制類型轉(zhuǎn)換為子類才能調(diào)用
動(dòng)態(tài)類型: 在編譯的時(shí)候編譯器只會(huì)檢查當(dāng)前類型 對(duì)應(yīng)的類中有沒(méi)有需要調(diào)用的方法
在運(yùn)行時(shí),系統(tǒng)會(huì)自動(dòng)判斷a1的真實(shí)類型
例如:
Animal:動(dòng)物類
import <Foundation/Foundation.h>
@interface Animal : NSObject
{
int _age;
}
- (void)eat;
@end
import "Animal.h"
@implementation Animal
- (void)eat
{
NSLog(@"吃東西");
}
@end
Dog狗類脱衙,繼承動(dòng)物類:
import <Foundation/Foundation.h>
import "Animal.h"
@interface Dog : Animal
- (void)kanJia;
@end
import "Dog.h"
@implementation Dog
(void)eat
{
NSLog(@"啃骨頭");
}(void)kanJia
{
NSLog(@"看家, 旺旺叫");
}
@end
Animal *a1 = [Dog new];
[a1 eat];
編譯時(shí)因?yàn)閯?dòng)物有eat方法例驹,不報(bào)錯(cuò)鹃锈。但是運(yùn)行時(shí)判斷為dog對(duì)象,調(diào)用dog對(duì)象的eat方法
#####實(shí)例變量修飾符
@public 就是實(shí)例變量修飾符
@public
>可以在其它類中訪問(wèn)被public修飾的成員變量
>也可以在本類中訪問(wèn)被public修飾的成員變量
>可以在子類中訪問(wèn)父類中被public修飾的成員變量
@private
>不可以在其它類中訪問(wèn)被private修飾的成員變量
>可以在本類中訪問(wèn)被private修飾的成員變量
>不可以在子類中訪問(wèn)父類中被private修飾的成員變量
@protected
>不可以在其它類中訪問(wèn)被protected修飾的成員變量
>可以在本類中訪問(wèn)被protected修飾的成員變量
>可以在子類中訪問(wèn)父類中被protected修飾的成員變量
注意: 默認(rèn)情況下所有的實(shí)例變量都是protected
@package
>介于public和private之間的
如果是在其它包中訪問(wèn)那么就是private的
如果是在當(dāng)前代碼所在的包種訪問(wèn)就是public的
#####description
NSlog(@"",moumoumou)
%@是用來(lái)打印對(duì)象的, 其實(shí)%@的本質(zhì)是用于打印字符串
只要利用%@打印某個(gè)對(duì)象, 系統(tǒng)內(nèi)部默認(rèn)就會(huì)調(diào)用父類的description方法
調(diào)用該方法, 該方法會(huì)返回一個(gè)字符串, 字符串的默認(rèn)格式 <類的名稱: 對(duì)象的地址>
可以重寫description方法, 返回我們需要打印的內(nèi)容
只要利用%@打印對(duì)象, 就會(huì)調(diào)用description
如果通過(guò)%@打印對(duì)象就會(huì)調(diào)用-號(hào)開(kāi)頭的
如果通過(guò)%@打印類對(duì)象就會(huì)調(diào)用+號(hào)開(kāi)頭的
建議: 在description方法中盡量不要使用self來(lái)獲取成員變量
因?yàn)槿绻憬?jīng)常在description方法中使用self, 可能已不小心就寫成了 %@, self
如果在description方法中利用%@輸出self會(huì)造成死循環(huán),例如下面:
- (NSString *)description
{
return [NSString stringWithFormat:@"%@", self];
}
####@porperty
在Xocde4.4之前, 可以使用@porperty來(lái)代替getter/setter方法的聲明
也就是說(shuō)我們只需要寫上@porperty就不用寫getter/setter方法的聲明
@synthesize是一個(gè)編譯器指令, 它可以簡(jiǎn)化我們getter/setter方法的實(shí)現(xiàn)
什么是實(shí)現(xiàn):
在聲明后面寫上大括號(hào)就代表著實(shí)現(xiàn)
(1)在@synthesize后面告訴編譯器, 需要實(shí)現(xiàn)哪個(gè)@property生成的聲明
(2)告訴@synthesize, 需要將傳入的值賦值給誰(shuí)和返回誰(shuí)的值給調(diào)用者
如果在@synthesize后面沒(méi)有告訴系統(tǒng)將傳入的值賦值給誰(shuí), 系統(tǒng)默認(rèn)會(huì)賦值給和@synthesize后面寫得名稱相同的成員變量
例如:
@synthesize age;就代表實(shí)現(xiàn)age成員變量的get, set,不是_age.
從Xcode4.4以后apple對(duì)@property進(jìn)行了一個(gè)增強(qiáng), 以后只要利用一個(gè)@property就可以同時(shí)生成setter/getter方法的聲明和實(shí)現(xiàn)
沒(méi)有告訴@property要將傳入的參數(shù)賦值給誰(shuí), 默認(rèn)@property會(huì)將傳入的屬性賦值給_開(kāi)頭的成員變量
@property有一個(gè)弊端: 它只會(huì)生成最簡(jiǎn)單的getter/setter方法的聲明和實(shí)現(xiàn), 并不會(huì)對(duì)傳入的數(shù)據(jù)進(jìn)行過(guò)濾
如果想對(duì)傳入的數(shù)據(jù)進(jìn)行過(guò)濾, 那么我們就必須重寫getter/setter方法
如果不想對(duì)傳入的數(shù)據(jù)進(jìn)行過(guò)濾, 僅僅是提供一個(gè)方法給外界操作成員變量, 那么就可以使用@property
如果利用@property來(lái)生成getter/setter方法, 那么我們可以不寫成員變量, 系統(tǒng)會(huì)自動(dòng)給我們生成一個(gè)_開(kāi)頭的成員變量
注意: @property自動(dòng)幫我們生成的成員變量是一個(gè)私有的成員變量, 也就是說(shuō)是在.m文件中生成的, 而不是在.h文件中生成的
如果重寫了setter方法, 那么property就只會(huì)生成getter方法
如果重寫了getter方法, 那么property就只會(huì)生成setter方法
如果同時(shí)重寫了getter/setter方法, 那么property就不會(huì)自動(dòng)幫我們生成私有的成員變量
####id類型
id是一個(gè)數(shù)據(jù)類型, 并且是一個(gè)動(dòng)態(tài)數(shù)據(jù)類型
默認(rèn)情況下所有的數(shù)據(jù)類型都是靜態(tài)數(shù)據(jù)類型
靜態(tài)數(shù)據(jù)類型的特點(diǎn):
在編譯時(shí)就知道變量的類型,
知道變量中有哪些屬性和方法
在編譯的時(shí)候就可以訪問(wèn)這些屬性和方法,
并且如果是通過(guò)靜態(tài)數(shù)據(jù)類型定義變量, 如果訪問(wèn)了不屬于靜態(tài)數(shù)據(jù)類型的屬性和方法, 那么編譯器就會(huì)報(bào)錯(cuò)
動(dòng)態(tài)數(shù)據(jù)類型的特點(diǎn):
在編譯的時(shí)候編譯器并不知道變量的真實(shí)類型, 只有在運(yùn)行的時(shí)候才知道它的真實(shí)類型
并且如果通過(guò)動(dòng)態(tài)數(shù)據(jù)類型定義變量, 如果訪問(wèn)了不屬于動(dòng)態(tài)數(shù)據(jù)類型的屬性和方法, 編譯器不會(huì)報(bào)錯(cuò)
#####類的本質(zhì)
類其實(shí)也是一個(gè)對(duì)象, 這個(gè)對(duì)象會(huì)在這個(gè)類第一次被使用的時(shí)候創(chuàng)建
只要有了類對(duì)象, 將來(lái)就可以通過(guò)類對(duì)象來(lái)創(chuàng)建實(shí)例對(duì)象
實(shí)例對(duì)象中有一個(gè)isa指針, 指向創(chuàng)建自己的類對(duì)象
類對(duì)象中保存了當(dāng)前對(duì)象所有的對(duì)象方法
當(dāng)給一個(gè)實(shí)例對(duì)象發(fā)送消息的時(shí)候, 會(huì)根據(jù)實(shí)例對(duì)象中的isa指針去對(duì)應(yīng)的類對(duì)象中查找
![Snip20150623_6.png](http://upload-images.jianshu.io/upload_images/2264508-1a692817a227e781.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
啟動(dòng)過(guò)程:
只要程序啟動(dòng)就會(huì)將所有類的代碼加載到內(nèi)存中, 放到代碼區(qū)圆丹。load方法會(huì)在當(dāng)前類被加載到內(nèi)存的時(shí)候調(diào)用, 有且僅會(huì)調(diào)用一次躯喇。如果存在繼承關(guān)系, 會(huì)先調(diào)用父類的load方法, 再調(diào)用子類的load方法。當(dāng)當(dāng)前類第一次被使用的時(shí)候就會(huì)調(diào)用(創(chuàng)建類對(duì)象的時(shí)候)倦微。initialize方法在整個(gè)程序的運(yùn)行過(guò)程中只會(huì)被調(diào)用一次, 無(wú)論你使用多少次這個(gè)類都只會(huì)調(diào)用一次正压。initialize用于對(duì)某一個(gè)類進(jìn)行一次性的初始化蔑匣。initialize和load一樣, 如果存在繼承關(guān)系, 會(huì)先調(diào)用父類的initialize再調(diào)用子類的initialize棕诵。
#####內(nèi)存管理
野指針:
只要一個(gè)對(duì)象被釋放了, 我們就稱這個(gè)對(duì)象為 "僵尸對(duì)象"凿将。當(dāng)一個(gè)指針指向一個(gè)僵尸對(duì)象, 我們就稱這個(gè)指針為野指針牧抵。只要給一個(gè)野指針發(fā)送消息就會(huì)報(bào)錯(cuò)侨把。為了避免給野指針發(fā)送消息會(huì)報(bào)錯(cuò), 一般情況下, 當(dāng)一個(gè)對(duì)象被釋放后我們會(huì)將這個(gè)對(duì)象的指針設(shè)置為空指針。
#####@class
應(yīng)用場(chǎng)景1:
聲明一個(gè)類
應(yīng)用場(chǎng)景2:
防止循環(huán)拷貝获枝,造成死循環(huán)骇笔。(import引入會(huì)拷貝文件內(nèi)容笨触,如果.h1引入.h2,.h2又引入.h1,會(huì)造成循環(huán)拷貝)
#####Block
MRC:
返回值 (^block名字 )(參數(shù)列表)
取別名
typedef (^bieblock)(int,int)
就可以用 bieblock來(lái)定義新block,類型為上面
如:
bieblock block1粗俱;
block1 = ^(int value1, int value2){
return value1 + value2;
};
// 1.block中可以訪問(wèn)外面的變量
/*
int a = 10;
void (^myBlock)() = ^{
NSLog(@"a = %i", a);
};
myBlock();
打印a = 10
// 2.block中可以定義和外界同名的變量, 并且如果在block中定義了和外界同名的變量, 在block中訪問(wèn)的是block中的變量
/*
int a = 10;
void (^myBlock)() = ^{
int a = 20;
NSLog(@"a = %i", a);
};
myBlock();
*/
打印a = 20虚吟;
//
// 3.默認(rèn)情況下, 不可以在block中修改外界變量的值
// 因?yàn)閎lock中的變量和外界的變量并不是同一個(gè)變量
// 如果block中訪問(wèn)到了外界的變量, block會(huì)將外界的變量拷貝一份到堆內(nèi)存中
// 因?yàn)閎lock中使用的外界變量是copy的, 所以在調(diào)用之前修改外界變量的值, 不會(huì)影響到block中copy的值
/*
int a = 10;
NSLog(@"&a = %p", &a);
void (^myBlock)() = ^{
// a = 50;
NSLog(@"&a = %p", &a);
NSLog(@"a = %i", a);
};
a = 20;
myBlock();
*/
/*
// 如果想在block中修改外界變量的值, 必須在外界變量前面加上__block
// 如果在block中修改了外界變量的值, 會(huì)影響到外界變量的值
__block int a = 10;
NSLog(@"&a = %p", &a);
void (^myBlock)() = ^{
a = 50;
NSLog(@"&a = %p", &a);
NSLog(@"a = %i", a);
};
myBlock();
NSLog(@"a = %i", a);
*/
/*
// 默認(rèn)情況下block存儲(chǔ)在棧中, 如果對(duì)block進(jìn)行一個(gè)copy操作, block會(huì)轉(zhuǎn)移到堆中
// 如果block在棧中, block中訪問(wèn)了外界的對(duì)象, 那么不會(huì)對(duì)對(duì)象進(jìn)行retain操作
// 但是如果block在堆中, block中訪問(wèn)了外界的對(duì)象, 那么會(huì)對(duì)外界的對(duì)象進(jìn)行一次retain
// 如果在block中訪問(wèn)了外界的對(duì)象, 一定要給對(duì)象加上__block, 只要加上了__block, 哪怕block在堆中, 也不會(huì)對(duì)外界的對(duì)象進(jìn)行retain
// 如果是在ARC開(kāi)發(fā)中就需要在前面加上__weak
內(nèi)存管理:
MRC:管理block
總結(jié):只要block沒(méi)有引用外部局部變量,block放在全局區(qū)
只要Block引用外部局部變量,block放在棧里面.
block只能使用copy,不能使用retain,使用retain,block還是在棧里面
只要block沒(méi)有引用外部局部變量,block放在全局區(qū)
ARC:管理block
只要block引用外部局部變量,block放在堆里面
block使用strong.最好不要使用copy
#####copy屬性
// 如果是通過(guò)不可變對(duì)象調(diào)用了copy方法, 那么不會(huì)生成一個(gè)新的對(duì)象
// 原因: 因?yàn)樵瓉?lái)的對(duì)象是不能修改的, 拷貝出來(lái)的對(duì)象也是不能修改的
// 既然兩個(gè)都不能修改, 所以永遠(yuǎn)不能影響到另外一個(gè)對(duì)象, 那么已經(jīng)符合需求
// 所以: OC為了對(duì)內(nèi)存進(jìn)行優(yōu)化, 就不會(huì)生成一個(gè)新的對(duì)象(比如nsstring->nsstring)
正是因?yàn)檎{(diào)用copy方法有時(shí)候會(huì)生成一個(gè)新的對(duì)象, 有時(shí)候不會(huì)生成一個(gè)新的對(duì)象
所以: 如果沒(méi)有生成新的對(duì)象, 我們稱之為淺拷貝, 本質(zhì)就是指針拷貝
如果生成了新的對(duì)象, 我們稱之為深拷貝, 本質(zhì)就是會(huì)創(chuàng)建一個(gè)新的對(duì)象
// 3.注意點(diǎn): copy block之后引發(fā)循環(huán)引用(MRC中)
(
// 如果對(duì)象中的block又用到了對(duì)象自己, 那么為了避免內(nèi)存泄露, 應(yīng)該將對(duì)象修飾為_(kāi)_block
__block Person *p = [[Person alloc] init]; // 1
p.name = @"lnj";
NSLog(@"retainCount = %lu", [p retainCount]);
p.pBlock = ^{
NSLog(@"name = %@", p.name); // 2
};
NSLog(@"retainCount = %lu", [p retainCount]);
p.pBlock();
[p release]; // 1
)
![屏幕快照 2017-06-22 上午12.10.13.png](http://upload-images.jianshu.io/upload_images/2264508-a8b0c54130bc743c.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
![Uploading 屏幕快照 2017-06-22 上午12.15.06_880264.png . . .]