目錄
- 屬性
- 修飾詞
- 循環(huán)引用
- typeof與typedef
1. 屬性
objc所有類和對象都是c結(jié)構(gòu)體转绷,category當(dāng)然也一樣
成員變量與實(shí)例變量
成員變量就是我們寫在花括中的變量, 聲明方法的括號中的 NSString *a 和 NSInteger b 都是成員變量捺癞。
@interface MyObject : NSObject {
NSString *a丸升;
NSInteger b蜕乡;
}
@end
實(shí)例變量是成員變量的一部分空民,雖然a 和b都是成員變量,但是它們是不同的酪呻,a是一個對象指針(前面帶*的)减宣,a又被稱之為實(shí)例變量,成員變量包含實(shí)例變量玩荠。
成員變量中除了b這樣的基本數(shù)據(jù)類型漆腌,其它的都是實(shí)例變量贼邓;
屬性本質(zhì)(@property)
@property (assign, nonatomic) NSString *a;
@property = ivar + getter + setter;
- 屬性就是通過@property 定義的變量,默認(rèn)會生成帶下劃線的成員變量闷尿,在這里xcode編譯器會自動生成一個成員變量 NSString *_a塑径。
- 屬性相比較于成員變量添加了存取方法(通過@synthesize,它是Xcode自動添加的)填具。
- 工程師可以使用@dynamic自己實(shí)現(xiàn)成員變量的getter和setter方法 , @synthesize 是讓 Xcode 幫你生成getter和setter方法 统舀。
@synthesize和@dynamic
- @property有兩個對應(yīng)的詞,一個是 @synthesize劳景,一個是 @dynamic誉简。如果 @synthesize和 @dynamic都沒寫,那么默認(rèn)的就是@syntheszie var = _var;
@synthesize語法來指定實(shí)例變量的名字.
@implementation Person
@synthesize firstName = _myFirstName;
@synthesize lastName = _myLastName;
@end
- @synthesize 的語義是如果你沒有手動實(shí)現(xiàn) setter 方法和 getter 方法盟广,那么編譯器會自動為你加上這兩個方法闷串。
- @dynamic 告訴編譯器:屬性的 setter 與 getter 方法由用戶自己實(shí)現(xiàn),不自動生成筋量。(當(dāng)然對于 readonly 的屬性只需提供 getter 即可)烹吵。假如一個屬性被聲明為 @dynamic var,然后你沒有提供 @setter方法和 @getter 方法桨武,編譯的時候沒問題肋拔,但是當(dāng)程序運(yùn)行到 instance.var = someVar,由于缺 setter 方法會導(dǎo)致程序崩潰玻募;或者當(dāng)運(yùn)行到 someVar = var 時只损,由于缺 getter 方法同樣會導(dǎo)致崩潰。編譯時沒問題七咧,運(yùn)行時才執(zhí)行相應(yīng)的方法跃惫,這就是所謂的動態(tài)綁定。
@private艾栋、@public 爆存、 @protect
屬性和成員變量可以被@private、@public 蝗砾、 @protect 修飾先较。
- @private 表示這個類私有的 只允許該類內(nèi)部和該類的對象訪問,其它類和他的子類不累訪問悼粮。
- @protect 表示只允許該類和該類的子類訪問闲勺。
- @public 表示公共的,所有的對象都能訪問扣猫。
怎么簡單的理解getter和setter
- setter:給外部提供的一個修改內(nèi)部屬性值的接口菜循,調(diào)用setter方法可以做到修改內(nèi)部屬性值。
- getter:外界提供的一個查看內(nèi)部變量的接口申尤。
- self.name和 _name的區(qū)別:
2. 修飾詞
屬性(@property)根據(jù)屬性中的設(shè)定的修飾詞(retain, copy, assign)將會生成不同的setter方法勺远。這些不同的setter方法中橙喘,對指針和內(nèi)存地址的處理是不同的,決定了不同的修飾詞的用途胶逢。
assign
- assign適用于基本數(shù)據(jù)類型厅瞎,weak是適用于NSObject對象,并且是一個弱引用宪塔。
- assign其實(shí)也可以用來修飾對象磁奖,只是對象的計(jì)數(shù)器不會+1 囊拜,那么我們?yōu)槭裁床挥盟啬晨穑恳驗(yàn)楸籥ssign修飾的對象在釋放之后,指針的地址還是存在的冠跷,也就是說指針并沒有被置為nil南誊。如果在后續(xù)的內(nèi)存分配中,剛好分到了這塊地址蜜托,程序就會崩潰掉抄囚。
而weak修飾的對象在釋放之后,指針地址會被置為nil橄务。所以現(xiàn)在一般弱引用就是用weak幔托。
weak ( ARC )(對象)
- 弱指針是針對對象的修飾詞 , 就是說它不能修飾基本數(shù)據(jù)類型(int float) 。
- weak 修飾的引用計(jì)數(shù)器不會+1 , 也就是直接賦值 蜂挪。
- 弱引用是為打破循環(huán)引用而生的重挑,比如在Block中,block在copy的時候棠涮,會對內(nèi)部使用到的對象的引用技術(shù)+1谬哀,如果使用[self 方法名],那么就會有一個強(qiáng)指針指向self所在的class的內(nèi)存地址严肪,class的引用計(jì)數(shù)會+1史煎,這樣一來會導(dǎo)致class所在的內(nèi)存地址無法被釋放,造成內(nèi)存泄漏 驳糯。
- 它最被人所喜歡的原因是 它所指向的對象如果被銷毀 , 它會指向 nil . 從而不會出現(xiàn)野指針錯誤 篇梭。
注意:什么情況使用 weak 關(guān)鍵字?
在 ARC 中,在有可能出現(xiàn)循環(huán)引用的時候,往往要通過讓其中一端使用 weak 來解決,
比如: delegate 代理屬性
自身已經(jīng)對它進(jìn)行一次強(qiáng)引用,沒有必要再強(qiáng)引用一次,此時也會使用 weak,
自定義 IBOutlet 控件屬性一般也使用 weak酝枢;當(dāng)然恬偷,也可以使用strong。
strong
- 直接賦值并且對象的引用計(jì)數(shù)器 +1 隧枫。在ARC下喉磁,實(shí)例變量本身是強(qiáng)引用谓苟,當(dāng)ARC將傳入值賦給實(shí)例變量時,它會保留傳入的值协怒,釋放現(xiàn)有
實(shí)例變量的值涝焙。 - 在 ARC 里替代了 retain 的作用 .
retain ( MRC )
- release 舊對象( 舊對象計(jì)數(shù)器 -1 ) , retain 新對象( 新對象計(jì)數(shù)器 +1 ) , 然后指向新對象 .
copy
- 如果是不可變的值,行為與strong相同孕暇。
- 如果是可變的值仑撞,會將一個副本賦給實(shí)例變量。當(dāng)一個不可變類有一個可變的子類時
(NSString NSMutableString,NSArray NSMutableArray)可以防止setter 方法傳遞一個
可變的子類的對象妖滔。會導(dǎo)致我們在不知情的情況下修改對象的值隧哮。
注意:
1.修飾的屬性本身要不可變的。例如 NSMutableArray 采用 copy 修飾 , 在addObject時會出現(xiàn)Crash座舍, 因?yàn)镹SMutableArray的對象在copy 過后就會變成NSArray沮翔。如果需要copy NSMutableArray對象,用:mutablecopy曲秉。
2.遵守 NSCopying 協(xié)議的對象使用 .
**nonatomic **
- 不對set方法加同步鎖 .
- 性能好
- 線程不安全
atomic
- 原子屬性就是對生成的 set 方法加互斥鎖 @synchronized(鎖對象) .
@synchronized(self) { _delegate = delegate;} - 需要消耗系統(tǒng)資源 .
- 互斥鎖是利用線程同步實(shí)現(xiàn)的 , 意在保證同一時間只有一個線程調(diào)用 set 方法 .
- 其實(shí)還有 get 方法 , 要是同時 set 和 get 一起調(diào)用還是會有問題的 . 所以即使用了 atomic 修飾 還是不夠安全 .
readonly (只讀)
1.讓 Xcode 只生成get方法 .
2.不想把暴露的屬性被人隨便替換時 , 可以使用 .
readwrite (讀寫)(默認(rèn))
- 讓 Xcode 生成get/set方法 .
- 不用 readonly 修飾時 , 默認(rèn)就是 readwrite .
3. 循環(huán)引用
不知道大家有沒有聽過一句話:人生就像打電話采蚀,不是你先掛,就是我先掛承二。其實(shí)用到這里是在合適不過了榆鼠。假設(shè)有兩個人打電話,彼此都強(qiáng)引用的對方的電話號碼亥鸠,但是如果雙方都不想掛電話的話妆够,那么電話就會一直通著。在OC中也是同樣的道理负蚊,正常情況下神妹,當(dāng)一個類或者對象即將釋放或者retainCount=0的時候,就會調(diào)用dealloc方法盖桥,釋放相應(yīng)的內(nèi)存灾螃。但是,當(dāng)這個對象被其他對象強(qiáng)引用的時候揩徊,那么它就不會被回收腰鬼。此時如果處理不當(dāng)?shù)脑捑蜁斐裳h(huán)引用。
循環(huán)引用對 app 有潛在的危害塑荒,會使內(nèi)存消耗過高熄赡,性能變差和 app 閃退等。
循環(huán)引用有哪些具體的情況齿税?block 彼硫? delegate ? NSTimer?
一、Block
block在copy時都會對block內(nèi)部用到的對象進(jìn)行強(qiáng)引用的。
該類又將block作為自己的屬性變量拧篮,而該類在block的方法體里面又使用了該類本身词渤,此時就很簡單的形成了一個環(huán)。
引發(fā)循環(huán)引用串绩,是因?yàn)楫?dāng)前self在強(qiáng)引用著block缺虐,而block又引用著self,這樣就造成了循環(huán)引用礁凡。而需不需要使用[weak self]就是由循環(huán)引用來決定,如果造成了循環(huán)引用,就必須使用[weak self]來打破循環(huán).
1.直接強(qiáng)引用
來分析一個自己設(shè)計(jì)的block模塊:
- 這種情況不必要弱引用
[self oneBlockSucess:^{
[self doSomething];
}];
- 這種情況就有必需用weakself來打破引用環(huán)
self.secondBlock = ^{
[self doSomething];
};
self.secondBlock說明當(dāng)前self持有了secondBlock這個block屬性,
所以如果在block回調(diào)內(nèi)想拿到self去做一些業(yè)務(wù)處理時,如果直接使用self,就會造成block持有了self,兩者互相持有,造成循環(huán)引用.打破這個環(huán),就要使用如typeof(self) __weak weakSelf = self;
的weakSelf方式;
2.間接強(qiáng)引用
再來分析一個自己設(shè)計(jì)的block模塊:
控制器self并沒有直接持有block屬性,但是卻強(qiáng)引用了bottomView,bottomView強(qiáng)引用了block屬性,這就造成了間接循環(huán)引用. block回調(diào)內(nèi)必須使用[weak self]來打破這個循環(huán),否則就會導(dǎo)致這個控制器self永遠(yuǎn)都不會被釋放掉產(chǎn)生常駐內(nèi)存高氮。如果self.bottomView與bottomView.block有一環(huán)不是強(qiáng)引用,就不會產(chǎn)生循環(huán)引用問題,因?yàn)椴粫纬梢粋€引用環(huán). 如果一個應(yīng)用程序里面你有很多循環(huán)引用,那么內(nèi)存占用就會比較大顷牌,并且由于一些特殊操作,會產(chǎn)生一個控制器的N個對象實(shí)例常駐內(nèi)存無法釋放,造成大量的系統(tǒng)內(nèi)存泄漏,這當(dāng)然是誰都不想看到的結(jié)果.
打破這個環(huán),就要使用如typeof(self) __weak weakSelf = self;的weakSelf方式;
3. 還有一些例子
//循環(huán)引用例子:比如控制器在使用一個Block剪芍,這個block又在使用控制器就會出現(xiàn)循環(huán)引用
- (void)setcycle
{
//例子1.
NSMutableArray *firstArray = [NSMutableArray array];
NSMutableArray *secondArray = [NSMutableArray array];
[firstArray addObject:secondArray];
[secondArray addObject:firstArray];
//例子2.
//代碼解釋:定義一個和self相同數(shù)據(jù)類型的bself ,并賦值為self窟蓝,在block中使用
__weak typeof (self) weakSelf = self;
/*
注意: typeof 括號中的值和等于后面的值是相同的類型罪裹。
__weak typeof(self.contentView) ws = self.contentView;
*/
self.passValueBlock = ^(NSString *str){
//循環(huán)引用1
[self test];
//解決方法:
//[weakSelf test];
//以下調(diào)用注釋掉的代碼同樣會造成循環(huán)引用,因?yàn)椴还苁峭ㄟ^self.blockString還是_blockString疗锐,或是函數(shù)調(diào)用[self doSomething]坊谁,因?yàn)橹灰?block中用到了對象的屬性或者函數(shù),block就會持有該對象而不是該對象中的某個屬性或者函數(shù)滑臊。
//NSString *localString = self.blockString;//循環(huán)引用2
//NSString *localString = _blockString;//循環(huán)引用3
//解決方法
//NSString *localString = weakSelf.blockString;
};
self.passValueBlock(@"s");
//例子3.
//宏定義一個block
typedef void(^blockAct)();
//利用宏定義來定義變量
blockAct blockAct_1;
//定義一個block變量來實(shí)現(xiàn)
blockAct_1 = ^(){
[self test];
};
//調(diào)用block
blockAct_1();
}
- (void)test
{
NSLog(@"BLOCK");
}
4. 這里直接用一個需求來探究循環(huán)引用的問題:
如果我想在Block中延時來運(yùn)行某段代碼,這里就會出現(xiàn)一個問題箍铲,看這段代碼:
- (void)viewDidLoad {
[super viewDidLoad];
MitPerson*person = [[MitPerson alloc]init];
__weak MitPerson * weakPerson = person;
person.mitBlock = ^{
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[weakPerson test];
});
};
person.mitBlock();
}
直接運(yùn)行這段代碼會發(fā)現(xiàn)[weakPerson test];并沒有執(zhí)行雇卷,打印一下會發(fā)現(xiàn),weakPerson 已經(jīng)是 Nil 了颠猴,這是由于當(dāng)我們的 viewDidLoad 方法運(yùn)行結(jié)束关划,由于是局部變量,無論是 MitPerson 和 weakPerson 都會被釋放掉翘瓮,那么這個時候在 Block 中就無法拿到正真的 person 內(nèi)容了贮折。
按如下方法修改代碼:
- (void)viewDidLoad {
[super viewDidLoad];
MitPerson*person = [[MitPerson alloc]init];
__weak MitPerson * weakPerson = person;
person.mitBlock = ^{
__strong MitPerson * strongPerson = weakPerson;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[strongPerson test];
});
};
person.mitBlock();
}
這樣當(dāng)2秒過后,計(jì)時器依然能夠拿到想要的 person 對象资盅。
深入探究原理
這里將會對每行代碼逐步進(jìn)行說明
1调榄、開辟一段控件存儲 person 類對象內(nèi)容,創(chuàng)建 person 強(qiáng)指針呵扛。
MitPerson*person = [[MitPerson alloc]init];
2每庆、創(chuàng)建一個弱指針 weakPerson 指向person對象內(nèi)容
__weak MitPerson * weakPerson = person;
person.mitBlock = ^{
3、在 person 對象的 Block 內(nèi)部創(chuàng)建一個強(qiáng)指針來指向 person 對象今穿,為了保證當(dāng)計(jì)時器執(zhí)行代碼的時候缤灵,person 對象沒有被系統(tǒng)銷毀所以我們必須在系統(tǒng)內(nèi)部進(jìn)行一次強(qiáng)引用,并用 GCD 計(jì)時器引用 strongPerson,為了保留 person 對象腮出,在下面會對這里更加詳細(xì)的說明帖鸦。
__strong MitPerson * strongPerson = weakPerson;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[strongPerson test];
});
};
4、執(zhí)行 Block 代碼
person.mitBlock();
首先需要明白一些關(guān)于 Block 的概念:
- 默認(rèn)情況下胚嘲,block 是放在棧里面的
- 一旦blcok進(jìn)行了copy操作富蓄,block的內(nèi)存就會被放在堆里面
- 堆立面的block(被copy過的block)有以下現(xiàn)象:
- block內(nèi)部如果通過外面聲明的強(qiáng)引用來使用,那么block內(nèi)部會自動產(chǎn)生一個強(qiáng)引用指向所使用的對象慢逾。
- block內(nèi)部如果通過外面聲明的弱引用來使用立倍,那么block內(nèi)部會自動產(chǎn)生一個弱引用指向所使用的對象。
我們進(jìn)行這段代碼的目的:
- 首先侣滩,我們需要在 Block 塊中調(diào)用口注,person 對象的方法,既然是在 Block 塊中我們就應(yīng)該使用弱指針來引用外部變量君珠,以此來避免循環(huán)引用寝志。但是又會出現(xiàn)問題,什么問題呢策添?就是當(dāng)我計(jì)時器要執(zhí)行方法的時候材部,發(fā)現(xiàn)對象已經(jīng)被釋放了。
- 接下來就是為了避免 person 對象在計(jì)時器執(zhí)行的時候被釋放掉:那么為什么 person 對象會被釋放掉呢唯竹?因?yàn)闊o論我們的person強(qiáng)指針還是 weakPerson 弱指針都是局部變量乐导,當(dāng)執(zhí)行完ViewDidLoad 的時候,指針會被銷毀浸颓。對象只有被強(qiáng)指針引用的時候才不會被銷毀物臂,而我們?nèi)绻苯右猛獠康膹?qiáng)指針對象又會產(chǎn)生循環(huán)引用,這個時候我們就用了一個巧妙的代碼來完成這個需求产上。
- 首先在 person.mitBlock 引用外部 weakPerson棵磷,并在內(nèi)部創(chuàng)建一個強(qiáng)指針去指向 person 對象,因?yàn)樵趦?nèi)部聲明變量晋涣,Block 是不會強(qiáng)引用這個對象的仪媒,這也就在避免的 person.mitBlock 循環(huán)引用風(fēng)險(xiǎn)的同時,又創(chuàng)建出了一個強(qiáng)指針指向?qū)ο蟆?/li>
- 之后再用 GCD 延時器 Block 來引用相對于它來說是外部的變量 strongPerson 谢鹊,這時延時器 Block 會默認(rèn)創(chuàng)建出來一個強(qiáng)引用來引用 person 對象算吩,當(dāng) person.mitBlock 作用域結(jié)束之后 strongPerson 會跟著被銷毀,內(nèi)存中就僅剩下了 延時器 Block 強(qiáng)引用著 person 對象撇贺,2秒之后觸發(fā) test 方法赌莺,GCD Block 內(nèi)部方法執(zhí)行完畢之后,延時器和對象都被銷毀松嘶,這樣就完美實(shí)現(xiàn)了我們的需求艘狭。
二、Delegate
@property (nonatomic, weak) id <TestDelegate> delegate;
說白了就是循環(huán)使用的問題,假如我們是寫的strong,那么 兩個類之間調(diào)用代理就是這樣的
BViewController *bViewController = [[BViewController alloc] init];
bViewController.delegate = self; //假設(shè) self 是AViewController
[self.navigationController pushViewController:bViewController animated:YES];
假如是 strong 的情況
bViewController.delegate ===> AViewController (也就是 A 的引用計(jì)數(shù) + 1)
AViewController 本身又是引用了 <BViewControllerDelegate> ===> delegate 引用計(jì)數(shù) + 1.
導(dǎo)致: AViewController <======> Delegate 巢音,也就循環(huán)引用
Delegate創(chuàng)建并強(qiáng)引用了 AViewController遵倦;(strong ==> A 強(qiáng)引用、weak ==> 引用計(jì)數(shù)不變)
所以用 strong的情況下官撼,相當(dāng)于 Delegate 和 A 兩個互相引用梧躺,A 永遠(yuǎn)會有一個引用計(jì)數(shù) 1 不會被釋放,所以造成了永遠(yuǎn)不能被內(nèi)存釋放傲绣,因此weak是必須的掠哥。
** NSTimer**
self.timer = [NSTimer timerWithTimeInterval:_time < 2? DEFAULTTIME: _time target:self selector:@selector(nextPage) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes];
是有風(fēng)險(xiǎn)的:
- timer會強(qiáng)持有target, 同時NSRunLoop會去強(qiáng)持有timer
- 而你需要注意到, 通常我們是把timer當(dāng)做某一個類的實(shí)例的屬性去看待的, 也做了強(qiáng)持有操作
所以這樣做的結(jié)果是, 循環(huán)引用!于是基于以上代碼的寫法, 在系統(tǒng)自動dealloc那個實(shí)例的時候你會發(fā)現(xiàn), 實(shí)例與timer兩者都無法free秃诵!
但是续搀,如果你把
[_timer invalidate];
_timer = nil;
寫在除了該類的dealloc方法的其他地方, 然后在dealloc該類的實(shí)例對象之前調(diào)用, 再dealloc操作, 那是一點(diǎn)問題都沒有.
三、關(guān)于NSTimer循環(huán)引用的處理方法:
構(gòu)造一個中介類給NSTimer, 中介類代碼如下:
#import <Foundation/Foundation.h>
/// justForText
@interface NSSafeObject : NSObject
- (instancetype)initWithObject:(id)object;
- (instancetype)initWithObject:(id)object withSelector:(SEL)selector;
- (void)excute;
@end
#import "NSSafeObject.h"
@interface NSSafeObject()
{
__weak id _object;
SEL _sel;
}
@end
@implementation NSSafeObject
- (instancetype)initWithObject:(id)object
{
if (self = [super init]) {
_object = object;
_sel = nil;
}
return self;
}
- (instancetype)initWithObject:(id)object withSelector:(SEL)selector
{
if(self = [super init])
{
_object = object;
_sel = selector;
}
return self;
}
- (void)excute
{
if (_object && _sel && [_object respondsToSelector:_sel]) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
[_object performSelector:_sel withObject:nil];
#pragma clang diagnostic pop
}
}
調(diào)用:
有了這樣一個類這樣就符合我們編寫代碼的思維習(xí)慣, 你依然可以把NSTimer當(dāng)做某一個類的對象, 無需考慮循化引用問題, 而只需要在每次創(chuàng)建之初多創(chuàng)建一個中介對象,演示代碼如下
NSSafeObject * safeObj = [[NSSafeObject alloc]initWithObject:self withSelector:@selector(autoSendBarrage)];
_timer = [NSTimer scheduledTimerWithTimeInterval:0.5 target:safeObj selector:@selector(excute) userInfo:nil repeats:YES];
或者給NSTimer寫一個類目, 復(fù)寫我們經(jīng)常使用的那個NSTimer的類方法(scheduledTimerWithTimeInterval:target:selector:userInfo:repeats:)
并且將WeakObject這個類的創(chuàng)建封裝進(jìn)去菠净。
5.無需解決循環(huán)引用的閉包
- UIView Block動畫
[UIView animateWithDuration:0.2 animations:^{
self.alpha = 0.0f;
} completion:^(BOOL finished) {
[self stopLoading];
}];
- 同步執(zhí)行
[_array enumerateObjectsUsingBlock:^(TimelineMessageData *nodeData, NSUInteger index, BOOL *stop) {
[_tableView reloadRow:index inSection:kSectionOffset rowAnimation:UITableViewRowAnimationNone];
}];
-
大部分GCD方法
因?yàn)閟elf并沒有對GCD的block進(jìn)行持有禁舷,沒有形成循環(huán)引用。目前我還沒碰到使用GCD導(dǎo)致循環(huán)引用的場景毅往,如果某種場景self對GCD的block進(jìn)行了持有牵咙,則才有可能造成循環(huán)引用。
dispatch_async(dispatch_get_main_queue(), ^{
[self doSomething];
});
- block并不是屬性值攀唯,而是臨時變量
- (void)doSomething {
[self testWithBlock:^{
[self test];
}];
}
- (void)testWithBlock:(void(^)())block {
block();
}
- (void)test {
NSLog(@"test");
}
5. typeof與typedef
理解
- typeof 是一個一元運(yùn)算抚恒,放在一個運(yùn)算數(shù)之前螺句,運(yùn)算數(shù)可以是任意類型娶桦∈仙可以理解為:我們根據(jù)typeof()括號里面的變量糕档,自動識別變量類型并返回該類型双仍。
typeof 常見運(yùn)用于Block中愧杯,避免循環(huán)引用發(fā)生的問題瑞侮。
用法:
//定義一個和self相同數(shù)據(jù)類型的bself 碟嘴,并賦值為self溪食,在block中使用
__weak typeof (self) weakSelf = self;
注意: typeof 括號中的值和等于后面的值是相同的類型。
__weak typeof(self.contentView) ws = self.contentView;
- typedef:定義一種類型的別名娜扇,而不只是簡單的宏替換错沃。
typedef 常用于命名(枚舉和Block)
用法:
用法1-結(jié)構(gòu)體
//結(jié)構(gòu)體
typedef struct Myrect {
float width;
float height;
}myRect;
用法2-枚舉
//枚舉值 它是一個整形(int) 并且,它不參與內(nèi)存的占用和釋放,枚舉定義變量即可直接使用,不用初始化.
//在代碼中使用枚舉的目的只有一個,那就是增加代碼的可讀性.
typedef NS_ENUM (NSUInteger,direction){
left=0,
right=1,
top =2,
down =3
};
typedef NS_ENUM (NSUInteger,direction_2){
left2 = 0,
right2 = 1 << 0,
top2 = 1 << 1,
down2 = 1 << 2
};
用法3-Block
//1.重新起個名字
typedef void(^PassValueBlock)(NSString *);
@interface BlockViewController : BaseViewController
//2.聲明block屬性
@property(nonatomic,copy)PassValueBlock passValueBlock;