之前在某個地方看到說 Objective-C 并沒有絕對的私有變量和私有方法。開始挺納悶的。我們在 .m 中實(shí)現(xiàn)的方法,不在.h 中聲明,外界不就不能訪問到了嗎?
首先,我們先來看一下私有的定義:私有是指只能夠在本類內(nèi)部使用或訪問,但是不能在類的外部被訪問。
看起來好像沒有什么問題,不過以下的幾種方式的確打破了上面的規(guī)則。
訪問私有方法
在 Objective-C 中,對象調(diào)用方法是以發(fā)送消息的形式實(shí)現(xiàn)的。所有方法的調(diào)用最終都會轉(zhuǎn)化為發(fā)送消息的形式,原型如下:
id objc_msgSend(id self, SEL op, ...)
我們定義如下兩個類,實(shí)現(xiàn)子類訪問父類的 "私有方法"。
#import <objc/message.h>
// MMFather.h
@interface MMFather : NSObject
@end
// MMFather.m
@implementation MMFather
(void)run:(NSString *)param {
NSLog(@"Father run with %@", param);
}
@end
// MMSon.h
@interface MMSon : MMFather
@end
// MMSon.m
@implementation MMSon
@end
?
// main.m
int main(int argc, const char * argv[]) {
@autoreleasepool {
MMSon *son = [MMSon new];
?
// 方法一
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wundeclared-selector"
objc_msgSend(son, @selector(run:), @"iPhone");
#pragma clang diagnostic pop
?
// 方法二
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wundeclared-selector"
if ([son respondsToSelector:@selector(run:)]) {
[son performSelector:@selector(run:) withObject:@"iPhone"];
}
#pragma clang diagnostic pop
? }
return 0;
}
≈
我們聲明了兩個類,一個 MMFather 類,一個繼承自 MMFather 的 MMSon 類。在 MMFather.m 中實(shí)現(xiàn) run:
方法,但是并沒有在 MMFather.h 中公開。如果我們直接使用 Son 實(shí)例調(diào)用 run 方法編譯器會報(bào)錯。
但是我們使用上述兩種方法都能成功實(shí)現(xiàn)對父類私有方法的訪問。"-Wundeclared-selector" 是為了消除編譯器找不到方法的警告,該方式只能在確認(rèn)某個方式實(shí)現(xiàn)的前提下使用,否者會在運(yùn)行時奔潰。
Objective-C 為什么能夠?qū)崿F(xiàn)訪問「私有方法」呢?其實(shí)這跟 Objective-C 語言的動態(tài)特性有密切的關(guān)系,對象在運(yùn)行的時候才會去查找方法。Objective-C 對象有一個 isa 指針指向其父類,在向該實(shí)例發(fā)送消息的時候,若它自己不能識別回到父類中去查找該消息。
如下如所示:
具體有關(guān) Objective-C 對象模型的知識可以參看這篇:Objective-C對象模型及應(yīng)用
訪問私有變量
私有變量的定義類似私有方法,但是在 Objective-C 中,仍然可以通過 runtime 來實(shí)現(xiàn)對私有變量的訪問。
在 MMSon.m 中加入一個私有方法,在 main.m 中訪問:
@implementation MMFather {
NSString *_name;
}
// main.m
{
MMSon *son = [[MMSon alloc] initWithName:@"小白"];
Ivar nameIvar = class_getInstanceVariable([son class], "_name");
NSString *name = object_getIvar(son, nameIvar);
NSLog(@"son name: %@", name);
}
控制臺成功的打印出了:小白
綜上所述:Objective-C 的確是沒有絕對的私有方法和私有變量的,也正是因?yàn)?OC 的這種特性,可以讓開發(fā)者做更多 trick 的事。