前言
2016年6月7號(hào)開(kāi)始load/initalize/KVO/KVC/Block冤荆,并通過(guò)代碼實(shí)現(xiàn)
load/initalize
- NSObject是大多數(shù)Objective-C類層次結(jié)構(gòu)的根類仙粱,通過(guò)繼承NSObject對(duì)象寒瓦,擁有Objective-C運(yùn)行時(shí)對(duì)象能力基本的接口
- load/initalize 兩個(gè)方法,由每個(gè)類Objective - C運(yùn)行時(shí)自動(dòng)調(diào)用
load 簡(jiǎn)介
當(dāng)一個(gè)類或分類被添加到Objective-C運(yùn)行時(shí)既绩;會(huì)調(diào)用load方法锥惋,實(shí)現(xiàn)這個(gè)方法來(lái)加載執(zhí)行特定類的行為畏梆。
Discussion:
- load方法消息會(huì)被發(fā)送到動(dòng)態(tài)加載和靜態(tài)鏈接的類和類別,只有當(dāng)被加載新的類或類別實(shí)現(xiàn)該方法才可以響應(yīng)坏挠。
初始化時(shí)執(zhí)行順序如下:
- 對(duì)所有鏈接框架初始化 (#import)
- 加載所有l(wèi)oad方法
- 初始化所有C++ 靜態(tài)變量和靜態(tài)方法和使用attribute(constructor) 關(guān)鍵字聲明的函數(shù)
- 初始化所有鏈接到你的框架 (在Link Binary With Libraries導(dǎo)入的框架)
注意
- 該類的load方法調(diào)用在所有父類的load方法之后
- 該分類的load方法調(diào)用在對(duì)應(yīng)類的load方法之后
使用場(chǎng)景
在自定義實(shí)現(xiàn)load方法時(shí)芍躏,可以很安全地(線程安全)提前獲取其他毫無(wú)相關(guān)類消息,并且在消息還沒(méi)實(shí)現(xiàn)時(shí)降狠,手動(dòng)去實(shí)現(xiàn)該消息(也就是說(shuō)load方法常常應(yīng)用于Swizzling Method
操作)
initalize 簡(jiǎn)介
在初始化類之前收到第一個(gè)消息,也就是說(shuō)对竣,對(duì)該類進(jìn)行初始化時(shí)被調(diào)用
Discussion
- 在程序中向類或者與其繼承的任何子類發(fā)送第一條消息前,runtime會(huì)以線程安全的方式來(lái)向類發(fā)起initialize消息榜配。
- 父類會(huì)在子類之前收到這條消息否纬。如果子類沒(méi)有實(shí)現(xiàn)initialize方法,由于Runtime機(jī)制
將按順序調(diào)用實(shí)現(xiàn)該類繼承而來(lái)的父類initialize方法蛋褥,父類的initialize可能會(huì)被調(diào)用多次,針對(duì)這種情況解決方法代碼:
+ (void)initialize {
if (self == [ClassName self]) {
// ... do the initialization ...
}
}
使用該方法注意點(diǎn):
在一個(gè)線程安全的方式下進(jìn)行初始化時(shí)临燃,在不同的類initialize被調(diào)用的順序是不保證的,環(huán),使用initialize方法時(shí)應(yīng)盡量做少量且必要的工作谬俄。具體而言柏靶,initialize方法內(nèi)部實(shí)現(xiàn)了同步鎖如果在方法內(nèi)實(shí)現(xiàn)復(fù)雜的邏輯,容易會(huì)導(dǎo)致死鎖溃论。因此屎蜓,不應(yīng)該依賴于initialize復(fù)雜的初始化,而應(yīng)該限制它的簡(jiǎn)單钥勋,可以使用init炬转、new等方法實(shí)現(xiàn)初始化。
特別注意
initialize每個(gè)類只會(huì)被調(diào)用一次算灸,如果你想為類或分類實(shí)現(xiàn)自定義初始化方法可以使用load方法進(jìn)行Swizzling Method操作,后面會(huì)簡(jiǎn)單講下FDTemplateLayoutCell框架如何使用Swizzling Method
Method Swizzling 簡(jiǎn)介
- 使用Method Swizzling可以改變現(xiàn)有的Selector的實(shí)現(xiàn)一項(xiàng)處理技術(shù)扼劈,方法的調(diào)用在Objective - C中運(yùn)行時(shí),通過(guò)改變Selector映射到該類的底層函數(shù)調(diào)度表,來(lái)修改方法的實(shí)現(xiàn).
- 在Objective-C實(shí)現(xiàn)擴(kuò)展方法可以使用Category來(lái)覆蓋系統(tǒng)方法菲驴,系統(tǒng)會(huì)優(yōu)先調(diào)用Category中的代碼荐吵,然后在調(diào)用原類中的代碼,如果我們?cè)谝延械腃ategory想實(shí)現(xiàn)UITableViewDelegate/UITableViewSource代理方法,往往就會(huì)使用Method Swizzling赊瞬,而且很輕松做到這一點(diǎn)先煎,可以通過(guò)新建UITableView Category,在其分類使用+(load)Method Swizzling替換方法代理巧涧,例如調(diào)用reloadData等方法薯蝎,F(xiàn)DTemplateLayoutCell框架就是這么做的。
Method Swizzling應(yīng)用場(chǎng)景
跟蹤視圖控制器ViewDidLoad谤绳、viewWillAppear 占锯、viewDidAppear實(shí)現(xiàn),如果多個(gè)ViewController重復(fù)使用代碼,可以在UIViewController分類在load方法中替換其實(shí)現(xiàn)方法
實(shí)現(xiàn)UITableViewDelegate缩筛、UITableViewDataSource自定義代理方法消略,例如在編寫(xiě)框架時(shí),用到UITableViewDelegate歪脏、UITableViewDataSource代理疑俭,替換具體實(shí)現(xiàn)名稱
Swizzling應(yīng)該總是在dispatch_once中執(zhí)行
因?yàn)镸ethod Swizzling會(huì)改變?nèi)譅顟B(tài),需要提供一切預(yù)防措施給Runtime婿失。GCD的dispatch_once原子性就是一個(gè)很好的預(yù)防措施钞艇,無(wú)論有多少個(gè)線程,確保代碼塊恰好執(zhí)行一次豪硅。
Selectors, Methods, & Implementations
在Objective - C中,實(shí)現(xiàn) Selectors, Methods, Implementations是指在Runtime的特定方面哩照,實(shí)現(xiàn)消息發(fā)送時(shí)往往會(huì)用到他們
Selectors(typedef struct objc_selector *SEL):用于表示在運(yùn)行時(shí)方法的名稱。
Method(typedef struct objc_method Method
):表示類的方法
Implementation(typedef id (IMP)(id, SEL, ...)
): 這個(gè)數(shù)據(jù)類型是指向一個(gè)指針函數(shù)實(shí)現(xiàn)方法
Method Swizzling使用
//
// UIViewController+MethodSwizzlingCategoryExample.m
// LoadInitalizeKVOKVCBlockExample
//
// Created by lmj on 16/6/8.
// Copyright ? 2016年 linmingjun. All rights reserved.
//
#import "UIViewController+MethodSwizzlingCategoryExample.h"
#import <objc/runtime.h>
@implementation UIViewController (MethodSwizzlingCategoryExample)
+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken,^{
Class class= [self class];
SEL originalSelector = @selector(viewDidLoad);
SEL swizzledSelector = @selector(swizzledViwDidLoad);
// 通過(guò)class_getInstanceMethod()函數(shù)從當(dāng)前class對(duì)象中的method list獲取method結(jié)構(gòu)體
Method originalMethod = class_getInstanceMethod(class, originalSelector);
Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);
// 如果是類方法:
// Class class = object_getClass((id)self);
// ...
// Method originalMethod = class_getClassMethod(class, originalSelector);
// Method swizzledMethod = class_getClassMethod(class, swizzledSelector);
// 使用class_addMethod()函數(shù)對(duì)Method Swizzling做了一層驗(yàn)證懒浮,如果self沒(méi)有實(shí)現(xiàn)swizzledSelector交換的方法飘弧,會(huì)導(dǎo)致失敗
BOOL didAddMethod = class_addMethod(class, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod));
if (didAddMethod) { // 如果self沒(méi)有實(shí)現(xiàn)swizzledViwDidLoad方法识藤,class_replaceMethod向?qū)ο笏鶎俚念悇?dòng)態(tài)添加所需的selector:,如果swizzledSelector沒(méi)有實(shí)現(xiàn)次伶,
// class_replaceMethod痴昧,它有兩種不同的行為。當(dāng)類中沒(méi)有想替換的原方法時(shí)冠王,該方法會(huì)調(diào)用class_addMethod來(lái)為該類增加一個(gè)新方法赶撰,也因?yàn)槿绱耍琧lass_replaceMethod在調(diào)用時(shí)需要傳入types參數(shù)柱彻,而method_exchangeImplementations和method_setImplementation卻不需要豪娜。
class_replaceMethod(class,
swizzledSelector,
method_getImplementation(originalMethod),
method_getTypeEncoding(originalMethod));
} else {// 通過(guò)class_addMethod()的驗(yàn)證,如果self實(shí)現(xiàn)了swizzledViwDidLoad這個(gè)方法哟楷,class_addMethod()函數(shù)將會(huì)返回NO瘤载,進(jìn)行交換了。
method_exchangeImplementations(originalMethod, swizzledMethod);
}
});
}
#pragma mark - Method Swizzling
- (void)swizzledViwDidLoad {
// 在swizzledViwDidLoad方法內(nèi)部調(diào)用[self swizzledViwDidLoad];時(shí)卖擅,執(zhí)行的是UIViewController的viewDidLoad方法鸣奔。
// 該方法調(diào)用 [self swizzledViwDidLoad]; 不會(huì)造成死循環(huán);因?yàn)閟wizzledViwDidLoad已經(jīng)被指定為viewDidLoad方法磨镶,所以這里實(shí)質(zhì)調(diào)用的是[self viewDidLoad];方法溃蔫,經(jīng)過(guò)調(diào)試如果不使用這行代碼程序運(yùn)行結(jié)果相同,但是 Mattt Thompson指出這行代碼是為了防止?jié)撛诘奈kU(xiǎn)或麻煩琳猫,體現(xiàn)一個(gè)優(yōu)秀程序員的良好素養(yǎng).
[self swizzledViwDidLoad];
NSLog(@"swizzledViwDidLoad: %@", self);
}
@end
SEL originalSelector = @selector(viewDidLoad);
SEL swizzledSelector = @selector(swizzledViwDidLoad);
Method originalMethod = class_getInstanceMethod(class, originalSelector)
Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);
1.通過(guò)class_getInstanceMethod()函數(shù)從當(dāng)前class對(duì)象中的method list獲取method結(jié)構(gòu)體
BOOL didAddMethod = class_addMethod(class, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod));
2.使用class_addMethod()函數(shù)對(duì)Method Swizzling做了一層驗(yàn)證,如果ViewController沒(méi)有viewDidLoad(書(shū)寫(xiě)過(guò)程可能寫(xiě)成viewDidLoadd等錯(cuò)誤)私痹,swizzledSelector交換的方法脐嫂,會(huì)導(dǎo)致失敗,返回YES
method_exchangeImplementations(originalMethod, swizzledMethod);
3.通過(guò)class_addMethod()的驗(yàn)證紊遵,如果self實(shí)現(xiàn)了swizzledViwDidLoad這個(gè)方法账千,class_addMethod()函數(shù)將會(huì)返回NO,進(jìn)行交換了暗膜。
swizzledViwDidLoad方法內(nèi)調(diào)用[self swizzledViwDidLoad]原因:
- 在swizzledViwDidLoad方法內(nèi)部調(diào)用[self swizzledViwDidLoad];時(shí)匀奏,執(zhí)行的是UIViewController的viewDidLoad方法。
- 該方法調(diào)用 [self swizzledViwDidLoad]時(shí)候不會(huì)造成死循環(huán)学搜;因?yàn)閟wizzledViwDidLoad已經(jīng)被指定為viewDidLoad方法娃善,所以這里實(shí)質(zhì)調(diào)用的是[self viewDidLoad];方法,經(jīng)過(guò)調(diào)試如果不使用這行代碼程序運(yùn)行結(jié)果相同瑞佩,但是 Mattt Thompson指出這行代碼是為了防止?jié)撛诘奈kU(xiǎn)或麻煩聚磺,體現(xiàn)一個(gè)優(yōu)秀程序員的良好素養(yǎng).
FDTemplateLayoutCell使用Method Swizzling
-
FDIndexPathHeightCacheInvalidation(UIViewTableView Category)
+ (void)load {
// All methods that trigger height cache's invalidation(觸發(fā)高度緩存失效的所有方法)
SEL selectors[] = {
@selector(reloadData),
@selector(insertSections:withRowAnimation:),
@selector(deleteSections:withRowAnimation:),
@selector(reloadSections:withRowAnimation:),
@selector(moveSection:toSection:),
@selector(insertRowsAtIndexPaths:withRowAnimation:),
@selector(deleteRowsAtIndexPaths:withRowAnimation:),
@selector(reloadRowsAtIndexPaths:withRowAnimation:),
@selector(moveRowAtIndexPath:toIndexPath:)
};
// sizeof(selectors) / sizeof(SEL) = (9(數(shù)組大小))* SEL / SEL; 一種用C語(yǔ)言計(jì)算個(gè)數(shù)的常用的技巧
for (NSUInteger index = 0; index < sizeof(selectors) / sizeof(SEL); ++index) {
SEL originalSelector = selectors[index];
SEL swizzledSelector = NSSelectorFromString([@"fd_" stringByAppendingString:NSStringFromSelector(originalSelector)]);
Method originalMethod = class_getInstanceMethod(self, originalSelector);
Method swizzledMethod = class_getInstanceMethod(self, swizzledSelector);
method_exchangeImplementations(originalMethod, swizzledMethod);
}
}
- 為什么在項(xiàng)目調(diào)用reloadData調(diào)用自動(dòng)計(jì)算高度以及緩存,原因就在這里炬丸,在Objective-C實(shí)現(xiàn)擴(kuò)展方法可以使用Category來(lái)覆蓋系統(tǒng)方法瘫寝,系統(tǒng)會(huì)優(yōu)先調(diào)用Category中的代碼,然后在調(diào)用原類中的代碼,如果我們?cè)谝延械腃ategory想實(shí)現(xiàn)UITableViewDelegate/UITableViewSource代理方法,往往就會(huì)使用Method Swizzling焕阿,可以通過(guò)新建UITableView Category咪啡,在其分類使用+(load)Method Swizzling替換方法代理(內(nèi)部計(jì)算cell高度,緩存高度)暮屡,再執(zhí)行項(xiàng)目當(dāng)中UITableViewDelegate/UITableViewDataSource代理方法
- 寫(xiě)到這里撤摸,先寫(xiě)其他內(nèi)容,有空再對(duì)FDTemplateLayoutCell框架所有類的源碼進(jìn)行分析栽惶,并且實(shí)現(xiàn)擴(kuò)展
block
本章介紹了塊和變量之間的相互作用愁溜,包括內(nèi)存管理
block變量的類型
在block作為對(duì)象代碼中,變量可以用五種不同的方式處理外厂,其中有是三種標(biāo)準(zhǔn)類型的變量冕象,如同一個(gè)函數(shù):
- 全局變量,包括靜態(tài)局部變量
- 全局函數(shù)(在技術(shù)上不是變量)
- 在函數(shù)內(nèi)的局部變量
block還支持其他兩種類型的變量
以__block修飾的變量,block代碼塊是可變的汁蝶,任何引用快內(nèi)的代碼內(nèi)存會(huì)從棧區(qū)復(fù)制到堆區(qū)
通過(guò)const來(lái)修飾的變量
以下規(guī)則適用于block中使用變量
全局變量是可訪問(wèn)的渐扮,包括在同一Scope(范圍)里的靜態(tài)變量
傳遞給block參數(shù)都可以訪問(wèn)(就像函數(shù) 的參數(shù))
局部棧區(qū)(非靜態(tài))在同一Scope(范圍)里以const修飾的局部變量(在于block外的變量可以無(wú)縫地直接在block內(nèi)部使用,而且在block內(nèi)變量發(fā)生變化的不會(huì)體現(xiàn)到block外)
局部變量聲明在該block的范圍內(nèi),它的行為完全像一個(gè)函數(shù)的局部變量,每次調(diào)用block時(shí)掖棉,都會(huì)對(duì)變量進(jìn)行一次copy操作(從棧區(qū)Copy到堆區(qū))墓律,這些變量可以作為常量或在block內(nèi)作為引用變量
下面的示例演示了局部非靜態(tài)變量的使用:
- (void)blockExample1 {
int x = 123;
void (^printXAndY)(int) = ^(int y) {
printf("%d %d\n", x, y);
};
printXAndY(456);
}
// prints: 123 456
局部非靜態(tài)變量試圖將一個(gè)的值賦給塊內(nèi)作為一個(gè)新值會(huì)導(dǎo)致一個(gè)錯(cuò)誤:
- (void)blockExample2 {
int x = 123;
void (^printXAndY)(int) = ^(int y) {
x = x + y; // error
printf("%d %d\n", x, y);
};
}
// Error
你可以指定一個(gè)可變的具有讀寫(xiě)功能的輸入變量,通常?應(yīng)用__block存儲(chǔ)類型來(lái)聲明變量幔亥。也可以通過(guò)與__block數(shù)據(jù)類型相似的耻讽、但他們又是排斥的數(shù)據(jù)類型來(lái)聲明,例如寄存器帕棉、自動(dòng)针肥、靜態(tài)局部變量,
詳見(jiàn)地址:寄存器香伴、自動(dòng)慰枕、靜態(tài)關(guān)鍵字詳細(xì)說(shuō)明要允許一個(gè)變量在block內(nèi)改變,使用__block存儲(chǔ)類型來(lái)修飾變量即纲,如果在棧區(qū)聲明的block具帮,進(jìn)行任何的拷貝操作造成超出frame的末尾存儲(chǔ)時(shí),將會(huì)造成棧區(qū)的破壞
下面的例子演示了在一個(gè)給定的詞法范圍內(nèi)的多個(gè)塊可以同時(shí)使用一個(gè)共享變量作為一次優(yōu)化低斋,block開(kāi)始存儲(chǔ)在棧區(qū)蜂厅,可以使用Block_copy對(duì)block進(jìn)行copy操作(或在Objective-C會(huì)進(jìn)行一次copy操作(ARC機(jī)制)),變量將從棧區(qū)拷貝到堆區(qū)拔稳,因此葛峻,一個(gè)__block聲明的變量地址是由時(shí)間而改變的巴比。在這里有兩__block變量進(jìn)一步的限制:他們不能改變數(shù)組長(zhǎng)度术奖,并且不能包含C99可變長(zhǎng)度的數(shù)組結(jié)構(gòu)礁遵。
下面的例子說(shuō)明了如何使用一個(gè)__block變量:
__block int x = 123; // x lives in block storage
void (^printXAndY)(int) = ^(int y) {
x = x + y;
printf("%d %d\n", x, y);
};
printXAndY(456); // prints: 579 456
// x is now 579
下面的例子顯示了幾個(gè)類型的變量的相互作用:
對(duì)象和block變量
作為block變量提供Objective-C和C++對(duì)象和其他block的支持
Objective-C 對(duì)象
- 當(dāng)一個(gè)block被拷貝時(shí),它會(huì)對(duì)block內(nèi)使用的對(duì)象變量創(chuàng)建一個(gè)強(qiáng)引用采记。
- 如果在一個(gè)方法中實(shí)現(xiàn)并使用一個(gè)block:
1.如果你通過(guò)引用訪問(wèn)的實(shí)例變量佣耐,那么這個(gè)block會(huì)對(duì)self產(chǎn)生強(qiáng)引用。
2.如果你通過(guò)值來(lái)訪問(wèn)實(shí)例變量唧龄,那么這個(gè)block會(huì)對(duì)這個(gè)變量本身產(chǎn)生強(qiáng)引用兼砖。
下面的例子說(shuō)明了這兩種不同的情況
dispatch_async(queue, ^{
// instanceVariable is used by reference, a strong reference is made to self
doSomethingWithObject(instanceVariable);
});
id localVariable = instanceVariable;
dispatch_async(queue, ^{
/*
localVariable is used by value, a strong reference is made to localVariable
(and not to self).
*/
doSomethingWithObject(localVariable);
});
dispatch_async(queue, ^{
// instanceVariable is used by reference, a strong reference is made to self
doSomethingWithObject(instanceVariable);
});
id localVariable = instanceVariable;
dispatch_async(queue, ^{
/*
localVariable is used by value, a strong reference is made to localVariable
(and not to self).
*/
doSomethingWithObject(localVariable);
});
dispatch_async(queue, ^{
// instanceVariable is used by reference, a strong reference is made to self
doSomethingWithObject(instanceVariable);
});
id localVariable = instanceVariable;
dispatch_async(queue, ^{
/*
localVariable is used by value, a strong reference is made to localVariable
(and not to self).
*/
doSomethingWithObject(localVariable);
});
C ++對(duì)象
一般在block內(nèi)可以使用c++對(duì)象。
在一個(gè)成員函數(shù),成員變量和函數(shù)的引用是通過(guò)隱含的this指針既棺,從而出現(xiàn)可變進(jìn)口讽挟。
指針,從而出現(xiàn)可變。 如果一個(gè)塊被復(fù)制丸冕,則有兩點(diǎn)考慮:如果你有一個(gè)__block
存儲(chǔ)類的東西是一個(gè)基于堆棧的c++對(duì)象,那么平常copy
使用構(gòu)造函數(shù)耽梅。如果您使用任何其他的c++基于堆棧的對(duì)象在一塊,它必須有一個(gè)const copy
構(gòu)造函數(shù)。 然后復(fù)制使用c++對(duì)象的構(gòu)造函數(shù)胖烛。
- 如果你有一個(gè)__block存儲(chǔ)器修飾的類而且是基于堆棧的C++對(duì)象眼姐,則通常使用Copy構(gòu)造函數(shù)。如果你在block內(nèi)使用其他基于堆棧C++對(duì)象佩番,它必須有一個(gè)const拷貝構(gòu)造函數(shù)众旗。然后,對(duì)C ++對(duì)象的構(gòu)造函數(shù)使用一次Copy操作趟畏。
調(diào)用一個(gè)block
如果你聲明一個(gè)block作為一個(gè)變量,可以使用它作為一個(gè)函數(shù),如下兩個(gè)例子的使用
int (^oneFrom)(int) = ^(int anInt) {
return anInt - 1;
};
printf("1 from 10 is %d", oneFrom(10));
// Prints "1 from 10 is 9"
float (^distanceTraveled)(float, float, float) =
^(float startingSpeed, float acceleration, float time) {
float distance = (startingSpeed * time) + (0.5 * acceleration * time * time);
return distance;
};
float howFar = distanceTraveled(0.0, 9.8, 1.0);
printf: howFar:4.9
通常贡歧,你將一個(gè)block作為一個(gè)函數(shù)或一個(gè)方法的參數(shù)傳遞給一個(gè)block。在這些情況下赋秀,通常創(chuàng)建一個(gè)“inline(內(nèi)聯(lián))”塊艘款。
使用block作為函數(shù)參數(shù)
你可以通過(guò)一個(gè)block作為一個(gè)函數(shù)參數(shù),如同其他參數(shù)一樣沃琅。然而,在很多情況下蜘欲,你不需要聲明block益眉;相反,你只需簡(jiǎn)單地實(shí)現(xiàn)它們姥份,然后使用inline(內(nèi)聯(lián))它們作為一個(gè)參數(shù)郭脂。下面的例子使用了qsort_b功能。qsort_b類似于標(biāo)準(zhǔn)的qsort_r功能澈歉,以最后參數(shù)作為一個(gè)block展鸡。
char *myCharacters[3] = { "TomJohn", "George", "Charles Condomine" };
qsort_b(myCharacters, 3, sizeof(char *), ^(const void *l, const void *r) {
char *left = *(char **)l;
char *right = *(char **)r;
return strncmp(left, right, 1);
});
// Block implementation ends at "}"
// myCharacters is now { "Charles Condomine", "George", "TomJohn" }
下面的例子演示了如何使用block與dispatch_apply功能。 dispatch_apply聲明如下:
void dispatch_apply(size_t iterations, dispatch_queue_t queue, void (^block)(size_t));
功能塊用于提交多次調(diào)用調(diào)度隊(duì)列埃难。它需要三個(gè)參數(shù)莹弊,第一個(gè)指定的迭代次數(shù)來(lái)執(zhí)行涤久;二是指定一個(gè)被提交的隊(duì)列,第三個(gè)是block本身忍弛,而這個(gè)block本身又需要一個(gè)單一的參數(shù)作為當(dāng)前的迭代次數(shù)响迂。
可以使用dispatch_apply打印出迭代索引
#include <dispatch/dispatch.h>
size_t count = 10;
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_apply(count, queue, ^(size_t i) {
printf("%u\n", i);
});
printf: 0
2
1
3
4
5
6
7
8
9
使用block作為方法參數(shù)
Cocoa提供了一種方法使用block的數(shù)量。 你通過(guò)block作為一個(gè)方法的參數(shù)就像任何其他參數(shù)细疚。
NSArray *array = @[@"A", @"B", @"C", @"A", @"B", @"Z", @"G", @"are", @"Q"];
NSSet *filterSet = [NSSet setWithObjects: @"A", @"Z", @"Q", nil];
BOOL (^test)(id obj, NSUInteger idx, BOOL *stop);
test = ^(id obj, NSUInteger idx, BOOL *stop) {
if (idx < 5) {
if ([filterSet containsObject: obj]) {
return YES;
}
}
return NO;
};
NSIndexSet *indexes = [array indexesOfObjectsPassingTest:test];
NSLog(@"indexes: %@", indexes);
/*
Output:
indexes: <NSIndexSet: 0x10236f0>[number of indexes: 2 (in 2 ranges), indexes: (0 3)]
*/
拷貝block方法
你可以copy和release模塊:
也可以使用C函數(shù)蔗彤,如下兩個(gè)函數(shù)
Block_copy();
Block_release();
為了避免內(nèi)存泄漏,必須始終平衡Block_copy()
與Block_release()兩種操作
避免兩種block書(shū)寫(xiě)
避免block在for/while循環(huán)和判斷語(yǔ)句內(nèi) (that is, ^{ ... }),,表示該block的是局部數(shù)據(jù)結(jié)構(gòu)的地址疯兼,棧的局部數(shù)據(jù)結(jié)構(gòu)的范圍是封閉的復(fù)合語(yǔ)句然遏,所以你應(yīng)該避免在下面的例子中顯示的模式:
面試提問(wèn):
load 與 initialize 的區(qū)別
答:load :
(1)load方法在這個(gè)文件被程序裝載時(shí)調(diào)用 (2)如果一個(gè)類實(shí)現(xiàn)了load
方法,在調(diào)用這個(gè)方法前會(huì)首先調(diào)用父類的load (3)如果一個(gè)類沒(méi)有實(shí)現(xiàn)load
方法吧彪,那么就不會(huì)調(diào)用它父類的load方法(4)添加一個(gè)子類的分類時(shí)待侵,在分類添加load方法時(shí),調(diào)用順序parent->child->category(5)在Compile Sources中来氧,文件的排放順序就是其裝載順序诫给,自然也就是load方法調(diào)用的順序(除了父子類關(guān)系的描述)
(6)load方法是線程安全的,它內(nèi)部使用了鎖啦扬,所以我們應(yīng)該避免線程阻塞在load
方法中中狂。
initialize:(1)實(shí)例化一個(gè)對(duì)象時(shí)調(diào)用,只會(huì)調(diào)用一次(2)在initialize
方法內(nèi)部也會(huì)調(diào)用父類的方法扑毡,即使子類沒(méi)有實(shí)現(xiàn)initialize方法胃榕,也會(huì)調(diào)用父類的方法