1.@property 后面可以有哪些修飾符?
1.線程安全的: atomic, nonatomic
2.訪問權(quán)限的: readonly助泽, readwrite
3.內(nèi)存管理(ARC) assign啰扛,strong,weak嗡贺,copy
4.內(nèi)存管理(MRC)assign隐解, retain,copy
5.指定方法名稱: setter= getter=
2.什么情況使用 weak 關(guān)鍵字诫睬,相比 assign 有什么不同煞茫?
在 ARC 中,在有可能出現(xiàn)循環(huán)引用的時(shí)候,往往要通過讓其中一端使用 weak 來解決,比如: delegate 代理屬性
如果用weak聲明的變量在棧中就會自動清空,賦值為nil摄凡。
如果用assign聲明的變量在棧中可能不會自動賦值為nil续徽,就會造成野指針錯(cuò)誤!
assign一般用在基本數(shù)據(jù)類型上面, 比如int\double等
weak一般用在代理對象上面, 或者用來解決循環(huán)強(qiáng)引用的問題
3.怎么用 copy 關(guān)鍵字亲澡?
NSString钦扭、NSArray、NSDictionary 等經(jīng)常使用 copy 關(guān)鍵字谷扣,當(dāng)多個(gè)指針指向同一個(gè)對象時(shí),為避免一個(gè)指針對對象的改動對其他指針的使用產(chǎn)生影響,使用copy來創(chuàng)建對象的副本
如頁面間傳值使用copy,A向B控制器傳屬性(屬性為自定義對象),為避免因A的屬性變化對B的屬性產(chǎn)生影響
再如多人開發(fā)或封裝庫,在不明確傳入值為可變還是不可變的情況下,使用copy更安全土全。
block 使用copy關(guān)鍵字是從 MRC 遺留下來的“傳統(tǒng)”捎琐,在 MRC 中会涎,方法內(nèi)部的 block 是在棧區(qū)的,使用 copy 可以把它放到堆區(qū)瑞凑。在 ARC 中寫不寫都行末秃,對于 block 使用 copy 還是 strong 效果是一樣的,但寫上 copy 也無傷大雅籽御,還是能時(shí)刻提醒我們练慕,編譯器自動對 block 進(jìn)行了 copy 操作
4.這個(gè)寫法會出什么問題: @property (copy) NSMutableArray *array;
Copy 修飾 可變的對象的話, 會生成一個(gè)不可變的NSCFConstantSting對象,賦值給可變屬性,編譯沒問題, 調(diào)方法修改其內(nèi)容時(shí)崩潰. unrecognized selector sent to instance
可以使用mutableCopy去解決這個(gè)問題惰匙。
5.如何讓自己的類用 copy 修飾符?如何重寫帶 copy 關(guān)鍵字的 setter铃将?
若想令自己所寫的對象具有拷貝功能项鬼,則需實(shí)現(xiàn) NSCopying 協(xié)議。如果自定義的對象分為可變版本與不可變版本劲阎,那么就要同時(shí)實(shí)現(xiàn) NSCopying 與 NSMutableCopying 協(xié)議绘盟。
//重寫這個(gè)方法
- (id)copyWithZone:(NSZone *)zone
//重寫setter
- (void)setName:(NSString *)name {
_name = [name copy];
}
6.@property 的本質(zhì)是什么?ivar悯仙、getter龄毡、setter 是如何生成并添加到這個(gè)類中的
@property = ivar + getter + setter;
“屬性” (property)有兩大概念
ivar(實(shí)例變量)、存取方法(access method = getter + setter)锡垄。
//以下代碼
@interface Person : NSObject
@property NSString *firstName;
@property NSString *lastName;
@end
//等同于
@interface Person : NSObject
- (NSString *)firstName;
- (void)setFirstName:(NSString *)firstName;
- (NSString *)lastName;
- (void)setLastName:(NSString *)lastName;
@end
//ivar沦零、getter、setter 是如何生成并添加到這個(gè)類中的?
編譯器會自動編寫訪問這些屬性所需的方法货岭,此過程叫做“自動合成”(autosynthesis)路操。
需要強(qiáng)調(diào)的是,這個(gè)過程由編譯器在編譯期執(zhí)行千贯,所以編輯器里看不到這些“合成方法”(synthesized method)的源代碼寻拂。
除了生成方法代碼 getter、setter 之外丈牢,編譯器還要自動向類中添加適當(dāng)類型的實(shí)例變量祭钉,并且在屬性名前面加下劃線,以此作為實(shí)例變量的名字己沛。
在前例中慌核,會生成兩個(gè)實(shí)例變量,其名稱分別為 _firstName 與 _lastName申尼。也可以在類的實(shí)現(xiàn)代碼里通過 @synthesize 語法來指定實(shí)例變量的名字.
@implementation Person
@synthesize firstName = _myFirstName;
@synthesize lastName = _myLastName;
@end
- @protocol 和 category 中如何使用 @property
在 protocol 中使用 property 只會生成 setter 和 getter 方法聲明,我們使用屬性的目的,是希望遵守我協(xié)議的對象能實(shí)現(xiàn)該屬性
category 使用 @property 也是只會生成 setter 和 getter 方法的聲明,如果我們真的需要給 category 增加屬性的實(shí)現(xiàn),需要借助于運(yùn)行時(shí)的兩個(gè)函數(shù):
objc_setAssociatedObject
objc_getAssociatedObject
8.runtime 如何實(shí)現(xiàn) weak 屬性
weak 此特質(zhì)表明該屬性定義了一種“非擁有關(guān)系” (nonowning relationship)垮卓。
為這種屬性設(shè)置新值時(shí),設(shè)置方法既不保留新值师幕,也不釋放舊值粟按。此特質(zhì)同 assign 類似, 然而在屬性所指的對象遭到摧毀時(shí)霹粥,屬性值也會清空(nil out)
runtime 對注冊的類灭将, 會進(jìn)行布局,對于 weak 對象會放入一個(gè) hash 表中后控。
用 weak 指向的對象內(nèi)存地址作為 key庙曙,當(dāng)此對象的引用計(jì)數(shù)為0的時(shí)候會 dealloc,假如 weak 指向的對象內(nèi)存地址是a浩淘,那么就會以a為鍵捌朴, 在這個(gè) weak 表中搜索吴攒,找到所有以a為鍵的 weak 對象,從而設(shè)置為 nil砂蔽。
{
id obj1 = [[NSObject alloc] init];
id __weak obj2 = obj1;
}
//轉(zhuǎn)換后
id obj2;
objc_initWeak(&obj2, obj1);
objc_destroyWeak(&obj2);
id objc_initWeak(id *location, id newObj) {
// 查看對象實(shí)例是否有效,無效對象直接導(dǎo)致指針釋放
if (!newObj) {
*location = nil;
return nil;
}
// 這里傳遞了三個(gè) bool 數(shù)值
// 使用 template 進(jìn)行常量參數(shù)傳遞是為了優(yōu)化性能
return storeWeak false/*old*/, true/*new*/, true/*crash*/>
(location, (objc_object*)newObj);
}
添加引用時(shí):objc_initWeak函數(shù)會調(diào)用 objc_storeWeak() 函數(shù)洼怔, objc_storeWeak() 的作用是更新指針指向,創(chuàng)建對應(yīng)的弱引用表
如果obj1的引用計(jì)數(shù)變?yōu)?左驾,obj1的內(nèi)存地址就會用作一個(gè)key去weak表中查詢茴厉,與之相關(guān)聯(lián)的value就會變成nil。weak表中的記錄會被清除什荣。
- @property中有哪些屬性關(guān)鍵字矾缓?/ @property 后面可以有哪些修飾符?
原子性--- nonatomic 特質(zhì)在默認(rèn)情況下稻爬,由編譯器合成的方法會通過鎖定機(jī)制確保其原子性(atomicity)嗜闻。如果屬性具備 nonatomic 特質(zhì),則不使用自旋鎖桅锄。
請注意琉雳,盡管沒有名為“atomic”的特質(zhì)(如果某屬性不具備 nonatomic 特質(zhì),那它就是“原子的” ( atomic) )友瘤,但是仍然可以在屬性特質(zhì)中寫明這一點(diǎn)翠肘,編譯器不會報(bào)錯(cuò)。若是自己定義存取方法辫秧,那么就應(yīng)該遵從與屬性特質(zhì)相符的原子性束倍。
讀/寫權(quán)限---readwrite(讀寫)、readonly (只讀)
內(nèi)存管理語義---assign盟戏、strong绪妹、 weak、unsafe_unretained柿究、copy
方法名---getter=<name> 邮旷、setter=<name>
不常用的:nonnull不能為空(用來修飾屬性,或者方法的參數(shù),方法的返回值)
null_resettable不能返回空, set可以為空(注意:如果使用null_resettable,必須 重寫get方法或者set方法,處理傳遞的值為空的情況)
nullable表示可以為空(使用方法和上面幾乎一樣,但是沒有發(fā)現(xiàn)和上面類似的宏)
- weak屬性需要在dealloc中置nil么蝇摸?
在ARC環(huán)境無論是強(qiáng)指針還是弱指針都無需在 dealloc 設(shè)置為 nil 婶肩, ARC 會自動幫我們處理
- @synthesize和@dynamic分別有什么作用?
1.@property有兩個(gè)對應(yīng)的詞貌夕,一個(gè)是 @synthesize律歼,一個(gè)是 @dynamic。如果 @synthesize和 @dynamic都沒寫蜂嗽,那么默認(rèn)的就是@syntheszie var = _var;
2.@synthesize 的語義是如果你沒有手動實(shí)現(xiàn) setter 方法和 getter 方法苗膝,那么編譯器會自動為你加上這兩個(gè)方法。
3.@dynamic 告訴編譯器:屬性的 setter 與 getter 方法由用戶自己實(shí)現(xiàn)植旧,不自動生成辱揭。(當(dāng)然對于 readonly 的屬性只需提供 getter 即可)。假如一個(gè)屬性被聲明為 @dynamic var病附,然后你沒有提供 @setter方法和 @getter 方法问窃,編譯的時(shí)候沒問題,但是當(dāng)程序運(yùn)行到 instance.var = someVar完沪,由于缺 setter 方法會導(dǎo)致程序崩潰域庇;或者當(dāng)運(yùn)行到 someVar = var 時(shí),由于缺 getter 方法同樣會導(dǎo)致崩潰覆积。編譯時(shí)沒問題听皿,運(yùn)行時(shí)才執(zhí)行相應(yīng)的方法,這就是所謂的動態(tài)綁定宽档。
- ARC下尉姨,不顯式指定任何屬性關(guān)鍵字時(shí),默認(rèn)的關(guān)鍵字都有哪些吗冤?
1.對應(yīng)基本數(shù)據(jù)類型默認(rèn)關(guān)鍵字是atomic,readwrite,assign
2.對于普通的 Objective-C 對象atomic,readwrite,strong
- 用@property聲明的NSString(或NSArray又厉,NSDictionary)經(jīng)常使用copy關(guān)鍵字,為什么椎瘟?如果改用strong關(guān)鍵字覆致,可能造成什么問題?
1.因?yàn)楦割愔羔樋梢灾赶蜃宇悓ο?使用 copy 的目的是為了讓本對象的屬性不受外界影響,使用 copy 無論給我傳入是一個(gè)可變對象還是不可對象,我本身持有的就是一個(gè)不可變的副本.
2.如果我們使用是 strong ,那么這個(gè)屬性就有可能指向一個(gè)可變對象,如果這個(gè)可變對象在外部被修改了,那么會影響該屬性.
@property (nonatomic ,readwrite, strong) NSArray *array;
NSArray *array = @[ @1, @2, @3, @4 ];
NSMutableArray *mutableArray = [NSMutableArray arrayWithArray:array];
self.array = mutableArray;
[mutableArray removeAllObjects];;
NSLog(@"%@",self.array);
[mutableArray addObjectsFromArray:array];
self.array = [mutableArray copy];
[mutableArray removeAllObjects];;
NSLog(@"%@",self.array);
//打印結(jié)果
2015-09-27 19:10:32.523 CYLArrayCopyDmo[10681:713670] (
)
2015-09-27 19:10:32.524 CYLArrayCopyDmo[10681:713670] (
1,
2,
3,
4
)
為了理解這種做法肺蔚,首先要知道煌妈,兩種情況:
1.對非集合類對象的 copy 與 mutableCopy 操作;
2.對集合類對象的 copy 與 mutableCopy 操作宣羊。
1. 對非集合類對象的copy操作:
在非集合類對象中:對
immutable
對象進(jìn)行 copy 操作声旺,是指針復(fù)制,mutableCopy 操作時(shí)內(nèi)容復(fù)制段只;對mutable
對象進(jìn)行 copy 和 mutableCopy 都是內(nèi)容復(fù)制腮猖。用代碼簡單表示如下:
[immutableObject copy] // 淺復(fù)制
[immutableObject mutableCopy] //深復(fù)制
[mutableObject copy] //深復(fù)制
[mutableObject mutableCopy] //深復(fù)制
比如以下代碼:
NSMutableString *string = [NSMutableString stringWithString:@"origin"];//copy
NSString *stringCopy = [string copy];
查看內(nèi)存,會發(fā)現(xiàn) string赞枕、stringCopy 內(nèi)存地址都不一樣澈缺,說明此時(shí)都是做內(nèi)容拷貝、深拷貝炕婶。即使你進(jìn)行如下操作:
[string appendString:@"origion!"]
stringCopy 的值也不會因此改變姐赡,但是如果不使用 copy,stringCopy 的值就會被改變柠掂。
用 @property 聲明 NSString项滑、NSArray、NSDictionary 經(jīng)常使用 copy 關(guān)鍵字涯贞,是因?yàn)樗麄冇袑?yīng)的可變類型:NSMutableString枪狂、NSMutableArray危喉、NSMutableDictionary,他們之間可能進(jìn)行賦值操作州疾,為確保對象中的字符串值不會無意間變動辜限,應(yīng)該在設(shè)置新屬性值時(shí)拷貝一份。
2严蓖、集合類對象的copy與mutableCopy
集合類對象是指 NSArray薄嫡、NSDictionary、NSSet ... 之類的對象颗胡。下面先看集合類immutable對象使用 copy 和 mutableCopy 的一個(gè)例子:
NSArray *array = @[@[@"a", @"b"], @[@"c", @"d"]];
NSArray *copyArray = [array copy];
NSMutableArray *mCopyArray = [array mutableCopy];
查看內(nèi)容毫深,可以看到 copyArray 和 array 的地址是一樣的,而 mCopyArray 和 array 的地址是不同的毒姨。說明 copy 操作進(jìn)行了指針拷貝哑蔫,mutableCopy 進(jìn)行了內(nèi)容拷貝。但需要強(qiáng)調(diào)的是:此處的內(nèi)容拷貝手素,僅僅是拷貝 array 這個(gè)對象鸳址,array 集合內(nèi)部的元素仍然是指針拷貝。
看 mutable 對象拷貝的例子:
NSMutableArray *array = [NSMutableArray arrayWithObjects:[NSMutableString stringWithString:@"a"],@"b",@"c",nil];
NSArray *copyArray = [array copy];
NSMutableArray *mCopyArray = [array mutableCopy];
查看內(nèi)存泉懦,如我們所料稿黍,copyArray、mCopyArray和 array 的內(nèi)存地址都不一樣崩哩,說明 copyArray巡球、mCopyArray 都對 array 進(jìn)行了內(nèi)容拷貝。同樣邓嘹,我們可以得出結(jié)論:
在集合類對象中酣栈,對 immutable 對象進(jìn)行 copy,是指針復(fù)制汹押, mutableCopy 是內(nèi)容復(fù)制矿筝;對 mutable 對象進(jìn)行 copy 和 mutableCopy 都是內(nèi)容復(fù)制。但是:集合對象的內(nèi)容復(fù)制僅限于對象本身棚贾,對象元素仍然是指針復(fù)制窖维。用代碼簡單表示如下:
[immutableObject copy] // 淺復(fù)制
[immutableObject mutableCopy] //單層深復(fù)制
[mutableObject copy] //單層深復(fù)制
[mutableObject mutableCopy] //單層深復(fù)制
- @synthesize合成實(shí)例變量的規(guī)則是什么?假如property名為foo妙痹,存在一個(gè)名為_foo的實(shí)例變量铸史,那么還會自動合成新變量么?
實(shí)例變量 = 成員變量 = ivar
@synthesize 合成實(shí)例變量的規(guī)則怯伊,有以下幾點(diǎn):
1.如果指定了成員變量的名稱,會生成一個(gè)指定的名稱的成員變量
2.如果這個(gè)成員已經(jīng)存在了就不再生成了
3.如果是 @synthesize foo; 還會生成一個(gè)名稱為foo的成員變量琳轿,也就是說:如果沒有指定成員變量的名稱會自動生成一個(gè)屬性同名的成員變量
- 在有了自動合成屬性實(shí)例變量之后,@synthesize還有哪些使用場景?
回答這個(gè)問題前崭篡,我們要搞清楚一個(gè)問題挪哄,什么情況下不會autosynthesis(自動合成)?
同時(shí)重寫了 setter 和 getter 時(shí)
重寫了只讀屬性的 getter 時(shí)
使用了 @dynamic 時(shí)
在 @protocol 中定義的所有屬性
在 category 中定義的所有屬性
重載的屬性
除了后三條媚送,對其他幾個(gè)我們可以總結(jié)出一個(gè)規(guī)律:當(dāng)你想手動管理 @property 的所有內(nèi)容時(shí)中燥,你就會嘗試通過實(shí)現(xiàn) @property 的所有“存取方法”(the accessor methods)或者使用 @dynamic 來達(dá)到這個(gè)目的寇甸,這時(shí)編譯器就會認(rèn)為你打算手動管理 @property塘偎,于是編譯器就禁用了 autosynthesis(自動合成)。
因?yàn)橛辛?autosynthesis(自動合成)拿霉,大部分開發(fā)者已經(jīng)習(xí)慣不去手動定義ivar吟秩,而是依賴于 autosynthesis(自動合成),但是一旦你需要使用ivar绽淘,而 autosynthesis(自動合成)又失效了涵防,如果不去手動定義ivar,那么你就得借助 @synthesize 來手動合成 ivar沪铭。
// 打開第14行和第17行中任意一行壮池,就可編譯成功
@import Foundation;
@interface CYLObject : NSObject
@property (nonatomic, copy) NSString *title;
@end
@implementation CYLObject {
// NSString *_title;
}
//@synthesize title = _title;
- (instancetype)init
{
self = [super init];
if (self) {
_title = @"微博@iOS程序犭袁";
}
return self;
}
- (NSString *)title {
return _title;
}
- (void)setTitle:(NSString *)title {
_title = [title copy];
}
@end
當(dāng)你同時(shí)重寫了 setter 和 getter 時(shí),系統(tǒng)就不會生成 ivar(實(shí)例變量/成員變量)杀怠。這時(shí)候有兩種選擇:
要么如第14行:手動創(chuàng)建 ivar
要么如第17行:使用@synthesize foo = _foo; 椰憋,關(guān)聯(lián) @property 與 ivar。
- objc中向一個(gè)nil對象發(fā)送消息將會發(fā)生什么赔退?
在 Objective-C 中向 nil 發(fā)送消息是完全有效的——只是在運(yùn)行時(shí)不會有任何作用:objc是動態(tài)語言橙依,每個(gè)方法在運(yùn)行時(shí)會被動態(tài)轉(zhuǎn)為消息發(fā)送,即:objc_msgSend(receiver, selector)硕旗。
// runtime.h(類在runtime中的定義)
// http://weibo.com/luohanchenyilong/
// https://github.com/ChenYilong
struct objc_class {
Class isa OBJC_ISA_AVAILABILITY; //isa指針指向Meta Class窗骑,因?yàn)镺bjc的類的本身也是一個(gè)Object缔逛,為了處理這個(gè)關(guān)系动漾,runtime就創(chuàng)造了Meta Class,當(dāng)給類發(fā)送[NSObject alloc]這樣消息時(shí)县爬,實(shí)際上是把這個(gè)消息發(fā)給了Class Object
#if !__OBJC2__
Class super_class OBJC2_UNAVAILABLE; // 父類
const char *name OBJC2_UNAVAILABLE; // 類名
long version OBJC2_UNAVAILABLE; // 類的版本信息墙基,默認(rèn)為0
long info OBJC2_UNAVAILABLE; // 類信息软族,供運(yùn)行期使用的一些位標(biāo)識
long instance_size OBJC2_UNAVAILABLE; // 該類的實(shí)例變量大小
struct objc_ivar_list *ivars OBJC2_UNAVAILABLE; // 該類的成員變量鏈表
struct objc_method_list **methodLists OBJC2_UNAVAILABLE; // 方法定義的鏈表
struct objc_cache *cache OBJC2_UNAVAILABLE; // 方法緩存,對象接到一個(gè)消息會根據(jù)isa指針查找消息對象碘橘,這時(shí)會在method Lists中遍歷互订,如果cache了,常用的方法調(diào)用時(shí)就能夠提高調(diào)用的效率痘拆。
struct objc_protocol_list *protocols OBJC2_UNAVAILABLE; // 協(xié)議鏈表
#endif
} OBJC2_UNAVAILABLE;
objc在向一個(gè)對象發(fā)送消息時(shí)仰禽,runtime庫會根據(jù)對象的isa指針找到該對象實(shí)際所屬的類,然后在該類中的方法列表以及其父類方法列表中尋找方法運(yùn)行,然后在發(fā)送消息的時(shí)候吐葵,objc_msgSend方法不會返回值规揪,所謂的返回內(nèi)容都是具體調(diào)用時(shí)執(zhí)行的。 那么温峭,回到本題猛铅,如果向一個(gè)nil對象發(fā)送消息,首先在尋找對象的isa指針時(shí)就是0地址返回了凤藏,所以不會出現(xiàn)任何錯(cuò)誤奸忽。
- objc中向一個(gè)對象發(fā)送消息[obj foo]和objc_msgSend()函數(shù)之間有什么關(guān)系?
#import "CYLTest.h"
int main(int argc, char * argv[]) {
@autoreleasepool {
CYLTest *test = [[CYLTest alloc] init];
[test performSelector:(@selector(iOSinit))];
return 0;
}
}
在終端輸入clang -rewrite-objc main.m
轉(zhuǎn)換后的代碼:
((void ()(id, SEL))(void )objc_msgSend)((id)obj, sel_registerName("foo"));
[obj foo];在objc編譯時(shí)揖庄,會被轉(zhuǎn)意為:objc_msgSend(obj, @selector(foo));栗菜。
- 什么時(shí)候會報(bào)unrecognized selector的異常?
當(dāng)調(diào)用該對象上某個(gè)方法,而該對象上沒有實(shí)現(xiàn)這個(gè)方法的時(shí)候蹄梢, 可以通過“消息轉(zhuǎn)發(fā)”進(jìn)行解決疙筹。
objc是動態(tài)語言,每個(gè)方法在運(yùn)行時(shí)會被動態(tài)轉(zhuǎn)為消息發(fā)送禁炒,即:objc_msgSend(receiver, selector)而咆。
objc在向一個(gè)對象發(fā)送消息時(shí),runtime庫會根據(jù)對象的isa指針找到該對象實(shí)際所屬的類幕袱,然后在該類中的方法列表以及其父類方法列表中尋找方法運(yùn)行暴备,如果,在最頂層的父類中依然找不到相應(yīng)的方法時(shí)凹蜂,程序在運(yùn)行時(shí)會掛掉并拋出異常unrecognized selector sent to XXX 馍驯。但是在這之前,objc的運(yùn)行時(shí)會給出三次拯救程序崩潰的機(jī)會:
1.Method resolution
objc運(yùn)行時(shí)會調(diào)用+resolveInstanceMethod:或者 +resolveClassMethod:玛痊,讓你有機(jī)會提供一個(gè)函數(shù)實(shí)現(xiàn)汰瘫。如果你添加了函數(shù),那運(yùn)行時(shí)系統(tǒng)就會重新啟動一次消息發(fā)送的過程擂煞,否則 混弥,運(yùn)行時(shí)就會移到下一步,消息轉(zhuǎn)發(fā)(Message Forwarding)对省。
2.Fast forwarding
如果目標(biāo)對象實(shí)現(xiàn)了-forwardingTargetForSelector:蝗拿,Runtime 這時(shí)就會調(diào)用這個(gè)方法,給你把這個(gè)消息轉(zhuǎn)發(fā)給其他對象的機(jī)會蒿涎。 只要這個(gè)方法返回的不是nil和self哀托,整個(gè)消息發(fā)送的過程就會被重啟,當(dāng)然發(fā)送的對象會變成你返回的那個(gè)對象劳秋。否則仓手,就會繼續(xù)Normal Fowarding胖齐。 這里叫Fast,只是為了區(qū)別下一步的轉(zhuǎn)發(fā)機(jī)制嗽冒。因?yàn)檫@一步不會創(chuàng)建任何新的對象呀伙,但下一步轉(zhuǎn)發(fā)會創(chuàng)建一個(gè)NSInvocation對象,所以相對更快點(diǎn)添坊。
3.Normal forwarding
這一步是Runtime最后一次給你挽救的機(jī)會剿另。首先它會發(fā)送-methodSignatureForSelector:消息獲得函數(shù)的參數(shù)和返回值類型。如果-methodSignatureForSelector:返回nil贬蛙,Runtime則會發(fā)出-doesNotRecognizeSelector:消息雨女,程序這時(shí)也就掛掉了。如果返回了一個(gè)函數(shù)簽名速客,Runtime就會創(chuàng)建一個(gè)NSInvocation對象并發(fā)送-forwardInvocation:消息給目標(biāo)對象戚篙。
- 一個(gè)objc對象如何進(jìn)行內(nèi)存布局五鲫?(考慮有父類的情況)
所有父類的成員變量和自己的成員變量都會存放在該對象所對應(yīng)的存儲空間中.
每一個(gè)對象內(nèi)部都有一個(gè)isa指針,指向他的類對象,類對象中存放著本對象的
對象方法列表(對象能夠接收的消息列表溺职,保存在它所對應(yīng)的類對象中)
成員變量的列表,
屬性列表,
- 一個(gè)objc對象的isa的指針指向什么?有什么作用位喂?
指向他的類對象,從而可以找到對象上的方法
- 下面的代碼輸出什么浪耘?
@implementation Son : Father
- (id)init
{
self = [super init];
if (self) {
NSLog(@"%@", NSStringFromClass([self class]));
NSLog(@"%@", NSStringFromClass([super class]));
}
return self;
}
@end
都輸出 Son
我們都知道:self 是類的隱藏參數(shù),指向當(dāng)前調(diào)用方法的這個(gè)類的實(shí)例塑崖。那 super 呢七冲?
很多人會想當(dāng)然的認(rèn)為“ super 和 self 類似,應(yīng)該是指向父類的指針吧规婆!”澜躺。這是很普遍的一個(gè)誤區(qū)。其實(shí) super 是一個(gè) Magic Keyword抒蚜, 它本質(zhì)是一個(gè)編譯器標(biāo)示符掘鄙,和 self 是指向的同一個(gè)消息接受者!他們兩個(gè)的不同點(diǎn)在于:super 會告訴編譯器嗡髓,調(diào)用 class 這個(gè)方法時(shí)操漠,要去父類的方法,而不是本類里的饿这。
上面的例子不管調(diào)用[self class]還是[super class]浊伙,接受消息的對象都是當(dāng)前 Son *xxx
這個(gè)對象。
當(dāng)使用 self 調(diào)用方法時(shí)长捧,會從當(dāng)前類的方法列表中開始找嚣鄙,如果沒有,就從父類中再找串结;而當(dāng)使用 super 時(shí)哑子,則從父類的方法列表中開始找廓八。然后調(diào)用父類的這個(gè)方法。
- runtime如何通過selector找到對應(yīng)的IMP地址赵抢?(分別考慮類方法和實(shí)例方法)
每一個(gè)類對象中都一個(gè)方法列表,方法列表中記錄著方法的名稱,方法實(shí)現(xiàn),以及參數(shù)類型,其實(shí)selector本質(zhì)就是方法名稱,通過這個(gè)方法名稱就可以在方法列表中找到對應(yīng)的方法實(shí)現(xiàn).
- objc中的類方法和實(shí)例方法有什么本質(zhì)區(qū)別和聯(lián)系剧蹂?
類方法:
1.類方法是屬于類對象的
2.類方法只能通過類對象調(diào)用
3.類方法中的self是類對象
4.類方法可以調(diào)用其他的類方法
5.類方法中不能訪問成員變量
6.類方法中不能直接調(diào)用對象方法
實(shí)例方法:
1.實(shí)例方法是屬于實(shí)例對象的
2.實(shí)例方法只能通過實(shí)例對象調(diào)用
3.實(shí)例方法中的self是實(shí)例對象
4.實(shí)例方法中可以訪問成員變量
5.實(shí)例方法中直接調(diào)用實(shí)例方法
6.實(shí)例方法中也可以調(diào)用類方法(通過類名)
- _objc_msgForward函數(shù)是做什么的,直接調(diào)用它將會發(fā)生什么烦却?
_objc_msgForward是 IMP 類型宠叼,用于消息轉(zhuǎn)發(fā)的:當(dāng)向一個(gè)對象發(fā)送一條消息,但它并沒有實(shí)現(xiàn)的時(shí)候其爵,_objc_msgForward會嘗試做消息轉(zhuǎn)發(fā)冒冬。
在“消息傳遞”過程中,objc_msgSend的動作比較清晰:首先在 Class 中的緩存查找 IMP (沒緩存則初始化緩存)摩渺,如果沒找到简烤,則向父類的 Class 查找。如果一直查找到根類仍舊沒有實(shí)現(xiàn)摇幻,則用_objc_msgForward函數(shù)指針代替 IMP 横侦。
對 objc-runtime-new.mm文件里與_objc_msgForward有關(guān)的三個(gè)函數(shù)使用偽代碼展示下:
id objc_msgSend(id self, SEL op, ...) {
if (!self) return nil;
IMP imp = class_getMethodImplementation(self->isa, SEL op);
imp(self, op, ...); //調(diào)用這個(gè)函數(shù),偽代碼...
}
//查找IMP
IMP class_getMethodImplementation(Class cls, SEL sel) {
if (!cls || !sel) return nil;
IMP imp = lookUpImpOrNil(cls, sel);
if (!imp) return _objc_msgForward; //_objc_msgForward 用于消息轉(zhuǎn)發(fā)
return imp;
}
IMP lookUpImpOrNil(Class cls, SEL sel) {
if (!cls->initialize()) {
_class_initialize(cls);
}
Class curClass = cls;
IMP imp = nil;
do { //先查緩存,緩存沒有時(shí)重建,仍舊沒有則向父類查詢
if (!curClass) break;
if (!curClass->cache) fill_cache(cls, curClass);
imp = cache_getImp(curClass, sel);
if (imp) break;
} while (curClass = curClass->superclass);
return imp;
}
_objc_msgForward是一個(gè)函數(shù)指針(和 IMP 的類型一樣)绰姻,是用于消息轉(zhuǎn)發(fā)的:當(dāng)向一個(gè)對象發(fā)送一條消息枉侧,但它并沒有實(shí)現(xiàn)的時(shí)候,_objc_msgForward會嘗試做消息轉(zhuǎn)發(fā)狂芋。
_objc_msgForward消息轉(zhuǎn)發(fā)做的幾件事:
1.調(diào)用resolveInstanceMethod:方法 (或 resolveClassMethod:)榨馁。允許用戶在此時(shí)為該 Class 動態(tài)添加實(shí)現(xiàn)。如果有實(shí)現(xiàn)了帜矾,則調(diào)用并返回YES翼虫,那么重新開始o(jì)bjc_msgSend流程。這一次對象會響應(yīng)這個(gè)選擇器屡萤,一般是因?yàn)樗呀?jīng)調(diào)用過class_addMethod珍剑。如果仍沒實(shí)現(xiàn),繼續(xù)下面的動作灭衷。
2.調(diào)用forwardingTargetForSelector:方法次慢,嘗試找到一個(gè)能響應(yīng)該消息的對象。如果獲取到翔曲,則直接把消息轉(zhuǎn)發(fā)給它迫像,返回非 nil 對象。否則返回 nil 瞳遍,繼續(xù)下面的動作闻妓。注意,這里不要返回 self 掠械,否則會形成死循環(huán)由缆。
3.調(diào)用methodSignatureForSelector:方法注祖,嘗試獲得一個(gè)方法簽名。如果獲取不到均唉,則直接調(diào)用doesNotRecognizeSelector拋出異常是晨。如果能獲取,則返回非nil:創(chuàng)建一個(gè) NSlnvocation 并傳給forwardInvocation:舔箭。
4.調(diào)用forwardInvocation:方法罩缴,將第3步獲取到的方法簽名包裝成 Invocation 傳入,如何處理就在這里面了层扶,并返回非nil箫章。
5.調(diào)用doesNotRecognizeSelector: ,默認(rèn)的實(shí)現(xiàn)是拋出異常镜会。如果第3步?jīng)]能獲得一個(gè)方法簽名檬寂,執(zhí)行該步驟。
- 能否向編譯后得到的類中增加實(shí)例變量戳表?能否向運(yùn)行時(shí)創(chuàng)建的類中添加實(shí)例變量桶至?為什么?
不能向編譯后得到的類中增加實(shí)例變量扒袖;
能向運(yùn)行時(shí)創(chuàng)建的類中添加實(shí)例變量塞茅;
1.因?yàn)榫幾g后的類已經(jīng)注冊在 runtime 中,類結(jié)構(gòu)體中的 objc_ivar_list 實(shí)例變量的鏈表 和 instance_size 實(shí)例變量的內(nèi)存大小已經(jīng)確定季率,同時(shí)runtime 會調(diào)用 class_setIvarLayout 或 class_setWeakIvarLayout 來處理 strong weak 引用。所以不能向存在的類中添加實(shí)例變量描沟;
2.運(yùn)行時(shí)創(chuàng)建的類是可以添加實(shí)例變量飒泻,調(diào)用 class_addIvar 函數(shù)。但是得在調(diào)用 objc_allocateClassPair 之后吏廉,objc_registerClassPair 之前泞遗,原因同上。
- objc使用什么機(jī)制管理對象內(nèi)存席覆?
通過 retainCount 的機(jī)制來決定對象是否需要釋放史辙。 每次 runloop 的時(shí)候,都會檢查對象的 retainCount佩伤,如果retainCount 為 0聊倔,說明該對象沒有地方需要繼續(xù)使用了,可以釋放掉了生巡。
27.ARC通過什么方式幫助開發(fā)者管理內(nèi)存耙蔑?
編譯時(shí)根據(jù)代碼上下文,插入 retain/release
28.不手動指定autoreleasepool的前提下孤荣,一個(gè)autorealese對象在什么時(shí)刻釋放甸陌?(比如在一個(gè)vc的viewDidLoad中創(chuàng)建)
分兩種情況:手動干預(yù)釋放時(shí)機(jī)须揣、系統(tǒng)自動去釋放。
手動干預(yù)釋放時(shí)機(jī)--指定autoreleasepool 就是所謂的:當(dāng)前作用域大括號結(jié)束時(shí)釋放钱豁。
系統(tǒng)自動去釋放--不手動指定autoreleasepool
Autorelease對象會在當(dāng)前的 runloop 迭代結(jié)束時(shí)釋放耻卡。
如果在一個(gè)vc的viewDidLoad中創(chuàng)建一個(gè) Autorelease對象,那么該對象會在 viewDidAppear 方法執(zhí)行前就被銷毀了牲尺。
29.BAD_ACCESS在什么情況下出現(xiàn)劲赠?
訪問了野指針,比如對一個(gè)已經(jīng)釋放的對象執(zhí)行了release秸谢、訪問已經(jīng)釋放對象的成員變量或者發(fā)消息凛澎。 死循環(huán)
30.蘋果是如何實(shí)現(xiàn)autoreleasepool的?
autoreleasepool以一個(gè)隊(duì)列數(shù)組的形式實(shí)現(xiàn),主要通過下列三個(gè)函數(shù)完成.
objc_autoreleasepoolPush
objc_autoreleasepoolPop
objc_aurorelease
看函數(shù)名就可以知道估蹄,對autorelease分別執(zhí)行push塑煎,和pop操作。銷毀對象時(shí)執(zhí)行release操作臭蚁。
31.使用block時(shí)什么情況會發(fā)生引用循環(huán)最铁,如何解決?
一個(gè)對象中強(qiáng)引用了block垮兑,在block中又使用了該對象冷尉,就會發(fā)射循環(huán)引用。 解決方法是將該對象使用__weak或者_(dá)_block修飾符修飾之后再在block中使用系枪。
id weak weakSelf = self;
或者 weak __typeof(&*self)weakSelf = self該方法可以設(shè)置宏
id __block weakSelf = self;
32.在block內(nèi)如何修改block外部變量雀哨?
默認(rèn)情況下,在block中訪問的外部變量是復(fù)制過去的私爷,即:寫操作不對原變量生效雾棺。但是你可以加上__block來讓其寫操作生效,示例代碼如下:
__block int a = 0;
void (^foo)(void) = ^{
a = 1;
}
f00();
//這里衬浑,a的值被修改為1
33.使用系統(tǒng)的某些block api(如UIView的block版本寫動畫時(shí))捌浩,是否也考慮引用循環(huán)問題?
系統(tǒng)的某些block api中工秩,UIView的block版本寫動畫時(shí)不需要考慮尸饺,但也有一些api 需要考慮。
所謂“引用循環(huán)”是指雙向的強(qiáng)引用助币,所以那些“單向的強(qiáng)引用”(block 強(qiáng)引用 self )沒有問題浪听,比如這些:
[UIView animateWithDuration:duration animations:^{
[self.superview layoutIfNeeded];
}];
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
self.someProperty = xyz;
}];
[[NSNotificationCenter defaultCenter] addObserverForName:@"someNotification"
object:nil
queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification * notification) {
self.someProperty = xyz; }];
但如果你使用一些參數(shù)中可能含有 ivar 的系統(tǒng) api ,如 GCD 奠支、NSNotificationCenter就要小心一點(diǎn):比如GCD 內(nèi)部如果引用了 self馋辈,而且 GCD 的其他參數(shù)是 ivar,則要考慮到循環(huán)引用:
__weak __typeof__(self) weakSelf = self;
dispatch_group_async(_operationsGroup, _operationsQueue, ^
{
__typeof__(self) strongSelf = weakSelf;
[strongSelf doSomething];
[strongSelf doSomethingElse];
} );
類似的:
__weak __typeof__(self) weakSelf = self;
_observer = [[NSNotificationCenter defaultCenter] addObserverForName:@"testKey"
object:nil
queue:nil
usingBlock:^(NSNotification *note) {
__typeof__(self) strongSelf = weakSelf;
[strongSelf dismissModalViewControllerAnimated:YES];
}];
34.GCD的隊(duì)列(dispatch_queue_t)分哪兩種類型倍谜?
串行隊(duì)列Serial Dispatch Queue
并行隊(duì)列Concurrent Dispatch Queue
35.如何用GCD同步若干個(gè)異步調(diào)用迈螟?(如根據(jù)若干個(gè)url異步加載多張圖片叉抡,然后在都下載完成后合成一張整圖)
使用Dispatch Group追加block到Global Group Queue,這些block如果全部執(zhí)行完畢,就會執(zhí)行Main Dispatch Queue中的結(jié)束處理的block答毫。
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, queue, ^{ /*加載圖片1 */ });
dispatch_group_async(group, queue, ^{ /*加載圖片2 */ });
dispatch_group_async(group, queue, ^{ /*加載圖片3 */ });
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
// 合并圖片
});
36.dispatch_barrier_async的作用是什么褥民?
在并行隊(duì)列中,為了保持某些任務(wù)的順序洗搂,需要等待一些任務(wù)完成后才能繼續(xù)進(jìn)行消返,使用 barrier 來等待之前任務(wù)完成,避免數(shù)據(jù)競爭等問題耘拇。 dispatch_barrier_async 函數(shù)會等待追加到Concurrent Dispatch Queue并行隊(duì)列中的操作全部執(zhí)行完之后撵颊,然后再執(zhí)行 dispatch_barrier_async 函數(shù)追加的處理,等 dispatch_barrier_async 追加的處理執(zhí)行結(jié)束之后惫叛,Concurrent Dispatch Queue才恢復(fù)之前的動作繼續(xù)執(zhí)行倡勇。
37.蘋果為什么要廢棄dispatch_get_current_queue?
dispatch_get_current_queue容易造成死鎖
38.以下代碼運(yùn)行結(jié)果如何嘉涌?
- (void)viewDidLoad
{
[super viewDidLoad];
NSLog(@"1");
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"2");
});
NSLog(@"3");
}
只輸出:1 妻熊。發(fā)生主線程鎖死。
39.addObserver:forKeyPath:options:context:各個(gè)參數(shù)的作用分別是什么仑最,observer中需要實(shí)現(xiàn)哪個(gè)方法才能獲得KVO回調(diào)扔役?
// 添加鍵值觀察
/*
1 觀察者,負(fù)責(zé)處理監(jiān)聽事件的對象
2 觀察的屬性
3 觀察的選項(xiàng)
4 上下文
*/
[self.person addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:@"Person Name"];
需要實(shí)現(xiàn)下面這個(gè)方法:
// 所有的 kvo 監(jiān)聽到事件警医,都會調(diào)用此方法
/*
1. 觀察的屬性
2. 觀察的對象
3. change 屬性變化字典(新/舊)
4. 上下文亿胸,與監(jiān)聽的時(shí)候傳遞的一致
*/
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context;
39.apple用什么方式實(shí)現(xiàn)對一個(gè)對象的KVO?
當(dāng)你觀察一個(gè)對象時(shí)法严,一個(gè)新的類會被動態(tài)創(chuàng)建损敷。這個(gè)類繼承自該對象的原本的類,并重寫了被觀察屬性的 setter 方法深啤。重寫的 setter 方法會負(fù)責(zé)在調(diào)用原 setter 方法之前和之后,通知所有觀察對象:值的更改路星。最后通過 isa 混寫(isa-swizzling) 把這個(gè)對象的 isa 指針 ( isa 指針告訴 Runtime 系統(tǒng)這個(gè)對象的類是什么 ) 指向這個(gè)新創(chuàng)建的子類溯街,對象就神奇的變成了新創(chuàng)建的子類的實(shí)例。
鍵值觀察通知依賴于 NSObject 的兩個(gè)方法: willChangeValueForKey: 和 didChangevlueForKey: 洋丐。在一個(gè)被觀察屬性發(fā)生改變之前呈昔, willChangeValueForKey: 一定會被調(diào)用,這就 會記錄舊的值友绝。而當(dāng)改變發(fā)生后堤尾, didChangeValueForKey: 會被調(diào)用,繼而 observeValueForKey:ofObject:change:context: 也會被調(diào)用迁客」Γ可以手動實(shí)現(xiàn)這些調(diào)用辞槐,但很少有人這么做。一般我們只在希望能控制回調(diào)的調(diào)用時(shí)機(jī)時(shí)才會這么做粘室。大部分情況下榄檬,改變通知會自動調(diào)用。
40.IBOutlet連出來的視圖屬性為什么可以被設(shè)置成weak?
因?yàn)榧热挥型怄溎敲匆晥D在xib或者storyboard中肯定存在衔统,視圖已經(jīng)對它有一個(gè)強(qiáng)引用了鹿榜。
不過這個(gè)回答漏了個(gè)重要知識,使用storyboard(xib不行)創(chuàng)建的vc锦爵,會有一個(gè)叫_topLevelObjectsToKeepAliveFromStoryboard的私有數(shù)組強(qiáng)引用所有top level的對象舱殿,所以這時(shí)即便outlet聲明成weak也沒關(guān)系
41.IB中User Defined Runtime Attributes如何使用?
它能夠通過KVC的方式配置一些你在interface builder 中不能配置的屬性险掀。當(dāng)你希望在IB中作盡可能多得事情沪袭,這個(gè)特性能夠幫助你編寫更加輕量級的viewcontroller