iOS開發(fā)經(jīng)驗(yàn)(11)-內(nèi)存

目錄

  1. 屬性
  2. 修飾詞
  3. 循環(huán)引用
  4. 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ū)別:
    • 前者:self.name = @"happy" 是通過調(diào)用setter方法設(shè)置屬性值癌幕,而 NSString *str = self.name 就是通過調(diào)用getter 方法獲取屬性值衙耕;
    • 后者:直接去訪問成員變量;
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;
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市雀瓢,隨后出現(xiàn)的幾起案子枢析,更是在濱河造成了極大的恐慌,老刑警劉巖刃麸,帶你破解...
    沈念sama閱讀 217,277評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件醒叁,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)把沼,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評論 3 393
  • 文/潘曉璐 我一進(jìn)店門啊易,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人饮睬,你說我怎么就攤上這事租谈。” “怎么了捆愁?”我有些...
    開封第一講書人閱讀 163,624評論 0 353
  • 文/不壞的土叔 我叫張陵割去,是天一觀的道長。 經(jīng)常有香客問我昼丑,道長呻逆,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,356評論 1 293
  • 正文 為了忘掉前任矾克,我火速辦了婚禮页慷,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘胁附。我一直安慰自己酒繁,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,402評論 6 392
  • 文/花漫 我一把揭開白布控妻。 她就那樣靜靜地躺著州袒,像睡著了一般。 火紅的嫁衣襯著肌膚如雪弓候。 梳的紋絲不亂的頭發(fā)上郎哭,一...
    開封第一講書人閱讀 51,292評論 1 301
  • 那天,我揣著相機(jī)與錄音菇存,去河邊找鬼夸研。 笑死,一個胖子當(dāng)著我的面吹牛依鸥,可吹牛的內(nèi)容都是我干的亥至。 我是一名探鬼主播,決...
    沈念sama閱讀 40,135評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼贱迟,長吁一口氣:“原來是場噩夢啊……” “哼姐扮!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起衣吠,我...
    開封第一講書人閱讀 38,992評論 0 275
  • 序言:老撾萬榮一對情侶失蹤茶敏,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后缚俏,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體惊搏,經(jīng)...
    沈念sama閱讀 45,429評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡贮乳,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,636評論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了胀屿。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片塘揣。...
    茶點(diǎn)故事閱讀 39,785評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖宿崭,靈堂內(nèi)的尸體忽然破棺而出亲铡,到底是詐尸還是另有隱情,我是刑警寧澤葡兑,帶...
    沈念sama閱讀 35,492評論 5 345
  • 正文 年R本政府宣布奖蔓,位于F島的核電站,受9級特大地震影響讹堤,放射性物質(zhì)發(fā)生泄漏吆鹤。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,092評論 3 328
  • 文/蒙蒙 一洲守、第九天 我趴在偏房一處隱蔽的房頂上張望疑务。 院中可真熱鬧,春花似錦梗醇、人聲如沸知允。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽温鸽。三九已至,卻和暖如春手负,著一層夾襖步出監(jiān)牢的瞬間涤垫,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評論 1 269
  • 我被黑心中介騙來泰國打工竟终, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留蝠猬,地道東北人。 一個月前我還...
    沈念sama閱讀 47,891評論 2 370
  • 正文 我出身青樓统捶,卻偏偏與公主長得像吱雏,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子瘾境,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,713評論 2 354

推薦閱讀更多精彩內(nèi)容