NSObject協(xié)議及本身詳解

?NSObject作為一個(gè)基類(lèi),這個(gè)類(lèi)遵守了NSObject協(xié)議旅敷,并且實(shí)現(xiàn)了NSObject協(xié)議里的所有方法生棍,所以NSObject類(lèi)及其子類(lèi)都可以調(diào)用這些方法。下面主要介紹NSObject協(xié)議里的屬性和方法媳谁。

一涂滴、NSObject協(xié)議

  • - (BOOL)isEqual:(id)object;:比較兩個(gè)對(duì)像是否相同。比較的是成員變量的值是否相同晴音,這與 ==符號(hào)有很大的不同:在對(duì)對(duì)象進(jìn)行比較時(shí)柔纵,==符號(hào)判斷是否是同一個(gè)對(duì)象,比較的是內(nèi)存地址锤躁。
UIImageView * imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"loli"]];
UIImage * image = [UIImage imageNamed:@"loli"];

NSLog(@"imageView.image == image : %d", (imageView.image == image));
NSLog(@"[imageView.image isEqual:image] : %d",[imageView.image isEqual:image]);

結(jié)果

imageView.image == image : 0
[imageView.image isEqual:image] : 1
  • @property (readonly) NSUInteger hash;:對(duì)象的哈希值搁料,只讀屬性,具有唯一性系羞。- (NSUInteger)hash方法只在對(duì)象被添加至NSSet和設(shè)置為NSDictionary的key時(shí)會(huì)調(diào)用加缘。

  • @property (readonly) Class superclass;:獲取父類(lèi)。

  • - (Class)class:獲取自身所屬的類(lèi)觉啊。

  • - (instancetype)self;:獲取對(duì)象自己拣宏。

  • - (id)performSelector:(SEL)aSelector;- (id)performSelector:(SEL)aSelector withObject:(id)object;杠人、- (id)performSelector:(SEL)aSelector withObject:(id)object1 withObject:(id)object2;:對(duì)象響應(yīng)某一方法勋乾,object是傳遞給響應(yīng)方法的參數(shù)。

    • performSelector是程序 運(yùn)行時(shí) 系統(tǒng)負(fù)責(zé)去找方法嗡善,在編譯時(shí)不做任何校驗(yàn)辑莫。如果方法由對(duì)象直接調(diào)用,編譯時(shí)會(huì)自動(dòng)校驗(yàn)罩引。Cocoa支持在運(yùn)行時(shí)向某個(gè)類(lèi)添加方法各吨,即方法編譯時(shí)不存在,但是運(yùn)行時(shí)候存在袁铐,這時(shí)候必然需要使用performSelector去調(diào)用揭蜒。所以有時(shí)候如果使用了performSelector横浑,為了使程序能正常運(yùn)行,一般會(huì)使用檢查方法- (BOOL)respondsToSelector:(SEL)aSelector;先行檢驗(yàn)一下屉更,針對(duì)不同情況做出正確的應(yīng)對(duì)措施徙融。
    • 這三個(gè)方法,均為同步執(zhí)行瑰谜,與線(xiàn)程無(wú)關(guān)欺冀,主線(xiàn)程和子線(xiàn)程中均可調(diào)用成功,等同于直接調(diào)用該方法萨脑。
- (void)viewDidLoad {
    [super viewDidLoad];
    [self performSelector:@selector(sendSomeThing:antherImage:) withObject:[UIImage imageNamed:@"boy"] withObject:[UIImage imageNamed:@"poem"]];
}

- (void)sendSomeThing:(UIImage *)aImage antherImage:(UIImage *)bImage
{
    NSLog(@"%@, %@", aImage, bImage);
}
  • - (BOOL)isProxy;:判斷是否是NSProxy的實(shí)例隐轩。

  • - (BOOL)isKindOfClass:(Class)aClass;:判斷某個(gè)對(duì)象是否是某個(gè)類(lèi)或者子類(lèi)的實(shí)例。

  • - (BOOL)isMemberOfClass:(Class)aClass;:判斷某個(gè)對(duì)象是否是某個(gè)類(lèi)的實(shí)例渤早。

- (void)viewDidLoad {
    [super viewDidLoad];
    self.view.backgroundColor = [UIColor whiteColor];
    UIImageView * imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"loli"]];
    NSLog(@"isKindOfClass : %d", [imageView isKindOfClass:[UIView class]]);
    NSLog(@"isMemberOfClass : %d", [imageView isMemberOfClass:[UIView class]]);
    NSLog(@"isMemberOfClass : %d", [imageView isMemberOfClass:[UIImageView class]]);
}

結(jié)果:

isKindOfClass : 1
isMemberOfClass : 0
isMemberOfClass : 1
  • - (BOOL)conformsToProtocol:(Protocol *)aProtocol;:是否遵循了某一協(xié)議龙助,至于是否實(shí)現(xiàn)了協(xié)議中的方法,該方法不關(guān)心蛛芥。
- (void)viewDidLoad {
    [super viewDidLoad];
    self.view.backgroundColor = [UIColor whiteColor];
    NSLog(@"UIViewAnimating : %d", [self conformsToProtocol:@protocol(UIViewAnimating)]);
    NSLog(@"UITableViewDelegate : %d", [self conformsToProtocol:@protocol(UITableViewDelegate)]);
}

結(jié)果:

UIViewAnimating : 1
UITableViewDelegate : 0
  • - (BOOL)respondsToSelector:(SEL)aSelector;:是否 能響應(yīng) 某一方法,若能則返回YES军援,不能返回NO仅淑。這里需要特別注意:類(lèi)對(duì)象(Object)只能響應(yīng)類(lèi)方法(+方法),實(shí)例對(duì)象(object)只能響應(yīng)實(shí)例方法(-方法)
- (void)viewDidLoad {
    [super viewDidLoad];
    self.view.backgroundColor = [UIColor whiteColor];
    NSLog(@"respondsToSelector : %d", [self respondsToSelector:@selector(viewWillAppear:)]);
    NSLog(@"respondsToSelector : %d", [self respondsToSelector:@selector(tableView:didSelectRowAtIndexPath:)]);
}

結(jié)果

respondsToSelector : 1
respondsToSelector : 0
  • @property (readonly, copy) NSString *description;胸哥、@property (readonly, copy) NSString *debugDescription;description是代碼打印輸出 實(shí)例 的時(shí)候調(diào)用的方法涯竟,debugDescription是控制臺(tái)也就是po的時(shí)候輸出實(shí)例的時(shí)候調(diào)用的方法,都可以自定義空厌。

?關(guān)于description/debugDescription方法(下面以description為例說(shuō)明):
?description方法默認(rèn)返回對(duì)象的描述信息(默認(rèn)實(shí)現(xiàn)是返回類(lèi)名和對(duì)象的內(nèi)存地址),這樣的話(huà),使用NSLog輸出OC對(duì)象,意義就不是很大,因?yàn)槲覀儾⒉魂P(guān)心對(duì)象的內(nèi)存地址,比較關(guān)心的是對(duì)象內(nèi)部的一些成變量的值庐船。因此,會(huì)經(jīng)常重寫(xiě)description方法,覆蓋description方法的默認(rèn)實(shí)現(xiàn)。
?description有一個(gè)實(shí)例方法-(NSString *)description;(NSLog輸出該類(lèi)的實(shí)例對(duì)象時(shí)調(diào)用)嘲更,一個(gè)類(lèi)方法+(NSString *)description;(NSLog輸出該類(lèi)的類(lèi)對(duì)象時(shí)調(diào)用)筐钟。

// 未重寫(xiě)description方法情況
Person * person_1 = [[Person alloc] init];
NSLog(@"%@", person_1);
NSLog(@"%@",[person_1 class]);
// 輸出結(jié)果
<Person: 0x60000003a700>
Person
// 重寫(xiě)description方法情況
// Person.m
-(NSString *)description
{
    return @"ASD";
}
+(NSString *)description
{
    return @"QWE";
}
// 其他類(lèi)調(diào)用
Person * person_1 = [[Person alloc] init];
NSLog(@"%@", person_1);
NSLog(@"%@",[person_1 class]);
// 輸出結(jié)果
ASD
QWE

特別注意:在重寫(xiě)的description方法中不能同時(shí)使用%@ 和self。

// 下面這種寫(xiě)法會(huì)導(dǎo)致循環(huán)引用赋朦,拋出異常
-(NSString *)description
{
    return [NSString stringWithFormat:@"%@", self];
}

但是有個(gè)例外情況當(dāng)在該方法內(nèi)部打印時(shí)同時(shí)使用會(huì)導(dǎo)致該方法連續(xù)調(diào)用三次后自動(dòng)結(jié)束篓冲。

// Person.m
-(NSString *)description
{
    NSLog(@"self = %@", self);
    return @"ASD";
}
// 其他類(lèi)調(diào)用
Person * person_1 = [[Person alloc] init];
NSLog(@"%@", person_1);
// 輸出結(jié)果
self = ASD
self = ASD
self = ASD
ASD

二、NSObject基類(lèi)

  • + (void)initialize;類(lèi)方法宠哄,在該類(lèi)收到第一條消息前初始化該類(lèi)壹将。

1、 -- > 一般情況下毛嫉,每個(gè)類(lèi)的這一方法只調(diào)用一次诽俯。

@implementation FatherView

+ (void)initialize
{
    NSLog(@"father -->  self == %@, functionString == %s", [self class], __FUNCTION__);
}

@end


@implementation OneViewController

- (void)viewWillAppear:(BOOL)animated
{
    FatherView * fatherView = [[FatherView alloc] init];
}

- (void)viewDidLoad {
    [super viewDidLoad];
    FatherView * fatherView = [[FatherView alloc] init];
}

@end

運(yùn)行打印結(jié)果只會(huì)打印一次:結(jié)果為

father -->  self == FatherView, functionString == +[FatherView initialize]

2、 -- > 超類(lèi)在它們的子類(lèi)之前收到這個(gè)消息承粤。

@implementation FatherView

+ (void)initialize
{
    NSLog(@"father -->  self == %@, functionString == %s", [self class], __FUNCTION__);
}

@end


@implementation BodyView

+ (void)initialize
{
    NSLog(@"body -->  self == %@, functionString == %s", [self class], __FUNCTION__);
}

@end


- (void)viewDidLoad {
    [super viewDidLoad];
    BodyView * bodyView = [[BodyView alloc] init];
}

運(yùn)行打印結(jié)果為:

father -->  self == FatherView, functionString == +[FatherView initialize]
body -->  self == BodyView, functionString == +[BodyView initialize]

3暴区、 -- > 如果子類(lèi)不實(shí)現(xiàn)+(void)initialize方法或者如果子類(lèi)顯式調(diào)用[super initialize]闯团,超類(lèi)的實(shí)現(xiàn)可能會(huì)被多次調(diào)用 。

// 如上面2颜启、中示例所示偷俭,將BodyView.m中的 + (void)initialize方法注釋掉再運(yùn)行。
// 結(jié)果為:
/**
father -->  self == FatherView, functionString == +[FatherView initialize]
father -->  self == BodyView, functionString == +[FatherView initialize]
*/

4缰盏、--> 如果想使類(lèi)本身+(void) initialize方法保護(hù)自己部分內(nèi)容不被多次運(yùn)行涌萤,可以按照以下方式構(gòu)建實(shí)現(xiàn):

@implementation FatherView

+ (void)initialize
{
    NSLog(@"會(huì)調(diào)用好幾次~~");
    if (self == [FatherView self]) {
        NSLog(@"father -->  self == %@, functionString == %s", [self class], __FUNCTION__);
    }
}

@end


@implementation BodyView

// 將該方法注銷(xiāo)掉
//+ (void)initialize
//{
//    NSLog(@"body -->  self == %@, functionString == %s", [self class], __FUNCTION__);
//}

@end


@implementation OneViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    BodyView * bodyView = [[BodyView alloc] init];
}

@end

運(yùn)行結(jié)果:

會(huì)調(diào)用好幾次~~
father -->  self == FatherView, functionString == +[FatherView initialize]
會(huì)調(diào)用好幾次~~

可以看到在第二次調(diào)用父類(lèi)的initialize方法時(shí)if內(nèi)部代碼沒(méi)有再次運(yùn)行。

5口猜、-- > 如果類(lèi)包含分類(lèi)负溪,且分類(lèi)重寫(xiě)initialize方法,那么則會(huì)調(diào)用分類(lèi)的 initialize 實(shí)現(xiàn)济炎,而原類(lèi)的該方法實(shí)現(xiàn)不會(huì)被調(diào)用川抡,這個(gè)機(jī)制同 NSObject 的其他方法一樣(除 + (void)load 方法外) 。在這不在舉例演示须尚。

  • + (void)load;每當(dāng)將類(lèi)或類(lèi)別添加到Objective-C runtime時(shí)調(diào)用崖堤。該方法在+(void)initialize方法后調(diào)用。

    • 超類(lèi)+(void)load方法先于子類(lèi)方法的調(diào)用耐床;

    • 如果分類(lèi)也實(shí)現(xiàn)了+(void)load方法密幔,那么該方法也會(huì)調(diào)用,但是是在類(lèi)本身的+(void)load方法調(diào)用之后撩轰!

@implementation BodyView

+ (void)load
{
    NSLog(@"body_load -->  self == %@, functionString == %s", [self class], __FUNCTION__);
}

@end


@implementation BodyView (ImageView)

+(void)load
{
    NSLog(@"BodyView+ImageView_load -->  self == %@, functionString == %s", [self class], __FUNCTION__);
}

@end

打印結(jié)果:

body_load -->  self == BodyView, functionString == +[BodyView load]
BodyView+ImageView_load -->  self == BodyView, functionString == +[BodyView(ImageView) load]

?


重點(diǎn)注意:無(wú)論是 +(void)initialize方法還是 +(void)load方法胯甩,均是在系統(tǒng)運(yùn)行時(shí)執(zhí)行的,調(diào)用會(huì)發(fā)生在-(BOOL)application:willFinishLaunchingWithOptions: 調(diào)用之前調(diào)用堪嫂≠梭铮可以依此對(duì)該方法進(jìn)行瘦身 -- > 可以看一下Sunny的這篇文章Notification Once,在此表示感謝皆串!淹办。

?

  • + (instancetype)alloc/ + (instancetype)allocWithZone:(struct _NSZone *)zone--在使用時(shí)參數(shù)傳nil /- (instancetype)init+ (instancetype)new:初始化方法

  • - (id)copy:返回NSCopying協(xié)議方法- (id)copyWithZone:(nullable NSZone *)zone;返回的對(duì)象恶复。NSObject本身不支持NSCopying協(xié)議娇唯,其子類(lèi)若想實(shí)現(xiàn)該方法就必須實(shí)現(xiàn)NSCopying協(xié)議并實(shí)現(xiàn)- (nonnull id)copyWithZone:(nullable NSZone *)zone方法,否者會(huì)報(bào)錯(cuò):-[子類(lèi) copyWithZone:]: unrecognized selector sent to instance ........

  • - (id)mutableCopy;:返回NSMutableCopying協(xié)議方法- (id)mutableCopyWithZone:(nullable NSZone *)zone在參數(shù)為nil情況下返回的對(duì)象寂玲。其子類(lèi)若想實(shí)現(xiàn)該方法就必須實(shí)現(xiàn)NSMutableCopying協(xié)議并實(shí)現(xiàn)- (id)mutableCopyWithZone:(nullable NSZone *)zone方法塔插,否者會(huì)報(bào)錯(cuò):-[子類(lèi) mutabelCopyWithZone:]: unrecognized selector sent to instance .......

    • 在這里引申出深拷貝和淺拷貝含義:

      • 淺拷貝:就是對(duì)指針的拷貝,但是目標(biāo)對(duì)象指針和源對(duì)象指針指向同一片內(nèi)存空間拓哟。
      • 深拷貝:是指拷貝對(duì)象的具體內(nèi)容想许,而內(nèi)存地址是自主分配的,拷貝結(jié)束之后,兩個(gè)對(duì)象的值雖然是相同的流纹,但是內(nèi)存地址不一樣糜烹,兩個(gè)對(duì)象互不影響,互不干涉漱凝。
      • 主要常見(jiàn)的涉及深拷貝淺拷貝的類(lèi)有:NSString疮蹦、NSMutableStringNSArray茸炒、NSMutableArray愕乎、NSDictionaryNSMutableDictionary壁公。它們都已經(jīng)遵循了NSCopying或者NSMutableCopying協(xié)議感论,對(duì)于不可變類(lèi)(NS····)copy即淺拷貝,mutableCopy即為深拷貝紊册;對(duì)于可變類(lèi)(NSMutable····)無(wú)論copy還是mutableCopy都是深拷貝比肄。
      • 其實(shí)對(duì)于所有(NS *)和(NSMutable *)上述結(jié)論都適用。對(duì)于所有父類(lèi)是NSObject的自定義類(lèi)而言囊陡,無(wú)論copy還是mutableCopy都是深拷貝芳绩。
  • - (void)dealloc:釋放系統(tǒng)無(wú)法釋放的對(duì)象占用的資源。常見(jiàn)關(guān)于dealloc的問(wèn)題:

    • 現(xiàn)在ARC下不需要調(diào)用[super dealloc];系統(tǒng)會(huì)自動(dòng)幫你調(diào)用撞反;

    • 可以用來(lái)釋放通知或者KVO的觀察者:通知中心是系統(tǒng)的單例妥色,當(dāng)在注冊(cè)通知的觀察者時(shí),實(shí)際上是在通知中心注冊(cè)的,這樣在界面dismiss后即使ARC下系統(tǒng)幫我們釋放了對(duì)象,但是在通知中心的觀察還是沒(méi)有移除痢畜,那么當(dāng)有該通知時(shí),依然會(huì)嘗試調(diào)用該對(duì)象的接受通知的方法鳍侣,這可能會(huì)導(dǎo)致一些問(wèn)題丁稀。在控制器中也可以在- (void)viewDidDisappear:(BOOL)animated中釋放。

    • 在控制器dismiss或者pop后dealloc不調(diào)用問(wèn)題:

      • 可能是使用了NSTimer導(dǎo)致的倚聚,需要在- (void)viewWillDisappear:(BOOL)animated;或者- (void)viewDidDisappear:(BOOL)animated;方法中調(diào)用[timer invalidate]即可线衫。
      • 代理屬性設(shè)置為strong的情況下可能發(fā)生循環(huán)引用問(wèn)題導(dǎo)致無(wú)法釋放,自然也不會(huì)調(diào)用該方法惑折。
      • 控制器中存在Block的循環(huán)引用問(wèn)題授账。也會(huì)造成dealloc方法不調(diào)用問(wèn)題。常見(jiàn)block中使用self的問(wèn)題 --- >__weak Viewcontroller * weakSelf = self;
  • + (BOOL)isSubclassOfClass:(Class)aClass:返回一個(gè)布爾值惨驶,該值指示接收類(lèi)是否是給定類(lèi)的子類(lèi)或完全相同白热。

  • + (NSUInteger)hash:類(lèi)對(duì)象的哈希值。

  • + (Class)superclass:返回接收者超類(lèi)的類(lèi)對(duì)象粗卜。

  • + (Class)class:返回類(lèi)對(duì)象屋确。

?


下面是與函數(shù)調(diào)用和runtime相關(guān)方法:OC是一門(mén)動(dòng)態(tài)語(yǔ)言,一個(gè)函數(shù)是由一個(gè)selector(SEL),和一個(gè)implement(IMP)組成的攻臀。Selector相當(dāng)于索引焕数,而Implement才是真正的函數(shù)實(shí)現(xiàn)。

  • + (BOOL)instancesRespondToSelector:(SEL)aSelector;:返回一個(gè)布爾值刨啸,指示類(lèi)本身是否能夠響應(yīng)給定的選擇器堡赔。注意與- (BOOL)respondsToSelector:(SEL)aSelector;方法的區(qū)別!I枇善已!用法參考上面respondsToSelector用法。

  • + (BOOL)conformsToProtocol:(Protocol *)protocol是否遵循了某一協(xié)議仑荐。詳細(xì)見(jiàn)上面object協(xié)議中方法描述雕拼,唯一不同即這是一個(gè)類(lèi)方法。

  • - (IMP)methodForSelector:(SEL)aSelector;粘招、+ (IMP)instanceMethodForSelector:(SEL)aSelector; 定位并返回接收方執(zhí)行方法的地址啥寇,以便將其作為函數(shù)調(diào)用。

    • IMP是真正的函數(shù)指針洒扎,SEL只是索引辑甜。

    • 參數(shù)SEL必須是有效的,所以可以在調(diào)用此方法之前使用- (BOOL)respondsToSelector:(SEL)aSelector;或者+ (BOOL)instancesRespondToSelector:(SEL)aSelector進(jìn)行檢驗(yàn)袍冷。

    • 如果調(diào)用者是一個(gè)實(shí)例磷醋,應(yīng)該參考一個(gè)實(shí)例方法; 如果調(diào)動(dòng)者是一個(gè)類(lèi),它應(yīng)該引用一個(gè)類(lèi)的方法:5讼摺!

為了方便了解下面的方法煌恢,下面先給出選擇器執(zhí)行函數(shù)的過(guò)程圖:

添加:
  • + (BOOL)resolveClassMethod:(SEL)sel:解析類(lèi)方法骇陈,動(dòng)態(tài)地為類(lèi)方法的給定選擇器提供實(shí)現(xiàn)。

  • + (BOOL)resolveInstanceMethod:(SEL)sel:解析實(shí)例方法瑰抵,動(dòng)態(tài)地為實(shí)例方法的給定選擇器提供實(shí)現(xiàn)你雌。

    ?對(duì)于上面兩個(gè)方法而言:

    • 如果參數(shù)方法被發(fā)現(xiàn)并且被添加到接收者,則返回YES二汛,否則婿崭,返回NO;
    • 這個(gè)函數(shù)在運(yùn)行時(shí)(runtime)如果沒(méi)有找到SEL的IML肴颊,就會(huì)執(zhí)行氓栈。這個(gè)函數(shù)是給類(lèi)利用class_addMethod添加函數(shù)的機(jī)會(huì)。
    • 如果類(lèi)重寫(xiě)了該函數(shù)婿着,并使用class_addMethod()添加了相應(yīng)的selector颤绕,并返回YES幸海,那么后面forwardingTargetForSelector等方法就不會(huì)被調(diào)用,如果在該函數(shù)中沒(méi)有添加相應(yīng)的selector奥务,那么不管返回什么物独,后面都會(huì)繼續(xù)調(diào)用相關(guān)方法或者爆出異常。
#import <objc/runtime.h>

void addNewMethod()
{
    NSLog(@"method == %s", __FUNCTION__);
}

@implementation BodyView

   + (BOOL)resolveInstanceMethod:(SEL)sel
{
    NSLog(@"解析實(shí)例方法 == %s", __FUNCTION__);
//    if (![self respondsToSelector:sel]) { //該方法中不可使用respondsToSelector:
//    或者instancesRespondToSelector:方法氯葬,這兩個(gè)方法參數(shù)SEL在類(lèi)中如果沒(méi)有實(shí)現(xiàn)挡篓,
//    也會(huì)引起resolveInstanceMethod方法的調(diào)用!V愠啤官研!
    if (sel == @selector(forwardMethod)) {
        class_addMethod([self class], sel, (IMP)addNewMethod, "v@:");
        NSLog(@"記錄次數(shù)");
        return YES;
    }
    return [super resolveInstanceMethod:sel];
}

@end
轉(zhuǎn)發(fā):
  • - (id)forwardingTargetForSelector:(SEL)aSelector返回?zé)o法識(shí)別的消息首先被指向的對(duì)象。

    • 當(dāng)某個(gè)對(duì)象不能接受某個(gè)selector時(shí)闯睹,將對(duì)該selector的調(diào)用轉(zhuǎn)發(fā)給另一個(gè)對(duì)象即返回的對(duì)象戏羽。但是該方法不建議返回nil(若返回nil則當(dāng)消息無(wú)法識(shí)別時(shí)會(huì)拋出異常)或者self(會(huì)造成死循環(huán))。這個(gè)方法在這一消息被一個(gè)更昂貴的forwardInvocation:方法接管之前楼吃,給了一個(gè)對(duì)象一個(gè)機(jī)會(huì)來(lái)重定向發(fā)送給它的未知消息始花。

    • 如果你在一個(gè)非root類(lèi)中實(shí)現(xiàn)這個(gè)方法,如果你的類(lèi)對(duì)于給定的選擇器沒(méi)有任何返回值孩锡,那么你應(yīng)該返回調(diào)用super的實(shí)現(xiàn)的結(jié)果酷宵。

    • 僅支持一個(gè)對(duì)象的返回,也就是說(shuō)消息只能被轉(zhuǎn)發(fā)給一個(gè)對(duì)象躬窜。

@interface FatherView : UIView

- (void)forwardMethod;

@end

@implementation FatherView

- (void)forwardMethod
{
    NSLog(@"fatherView ---- forwardMethod");
}

@end


@implementation BodyView

- (id)forwardingTargetForSelector:(SEL)aSelector
{
    FatherView * aView = [FatherView new];
    if ([aView respondsToSelector:@selector(forwardMethod)]) {
        return aView;
    }
    return [super forwardingTargetForSelector:aSelector];
}

@end


@implementation OneViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    BodyView * bodyView = [[BodyView alloc] init];
    [bodyView performSelector:@selector(forwardMethod)];
}

@end

如果上述例子中將BodyView中的- (id)forwardingTargetForSelector:(SEL)aSelector方法注釋掉浇垦,再次運(yùn)行會(huì)拋出異常-[BodyView forwardMethod]: unrecognized selector sent to instance *******

  • - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector;方法簽名。返回一個(gè)包含由給定選擇器方法標(biāo)識(shí)的描述的NSMethodSignature對(duì)象荣挨,如果aSelector沒(méi)找到就返回nil男韧。一般復(fù)寫(xiě)用于runtime提供方法簽名。

  • + (NSMethodSignature *)instanceMethodSignatureForSelector:(SEL)aSelector;同樣是方法簽名默垄,但是一般不復(fù)寫(xiě)此虑,只是調(diào)用,用于獲取類(lèi)方法簽名厕倍。

  • - (void)forwardInvocation:(NSInvocation *)anInvocation:由子類(lèi)覆蓋以將消息轉(zhuǎn)發(fā)給其他對(duì)象寡壮。

    • 為了響應(yīng)你的對(duì)象本身不能識(shí)別的方法贩疙,除了forwardInvocation:方法外讹弯,還必須重寫(xiě)- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector方法,轉(zhuǎn)發(fā)消息的機(jī)制是使用從methodSignatureForSelector:方法返回的信息創(chuàng)建要轉(zhuǎn)發(fā)的NSInvocation對(duì)象这溅。

    • forwardInvocation:方法的實(shí)現(xiàn)有兩個(gè)任務(wù):

      • 查找可以響應(yīng)參數(shù)anInvocation中編碼的消息的對(duì)象组民。
      • 向使用參數(shù)anInvocation的對(duì)象發(fā)送消息。參數(shù)anInvocation將會(huì)保存結(jié)果悲靴,運(yùn)行時(shí)系統(tǒng)將提取并將此結(jié)果傳遞給原始發(fā)件人臭胜。
      • 可以實(shí)現(xiàn)向多個(gè)目標(biāo)轉(zhuǎn)發(fā)消息。
@implementation FatherView

- (void)forwardMethod
{
    NSLog(@"fatherView ---- forwardMethod");
}

@end


@implementation BodyView

- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
{
    NSLog(@"%s", __FUNCTION__);
    if (aSelector == @selector(forwardMethod)) {
        return [NSMethodSignature signatureWithObjCTypes:"v@:"];
    }
    return nil;
}

- (void)forwardInvocation:(NSInvocation *)anInvocation
{
    NSLog(@"執(zhí)行轉(zhuǎn)換對(duì)象的方法 == %s", __FUNCTION__);
    SEL aSelector = [anInvocation selector];
    FatherView * aView = [FatherView new];
    if ([aView respondsToSelector:aSelector]){
        [anInvocation invokeWithTarget:aView];
    }else{
        [super forwardInvocation:anInvocation];
    }
//    if (anInvocation.selector == @selector(forwardMethod)) {
//        FatherView * aView = [FatherView new];
//        [aView performSelector:@selector(forwardMethod)];
//    }else{
//        [super forwardInvocation:anInvocation];
//    }
}

@end

// 調(diào)用
BodyView * bodyView = [[BodyView alloc] init];
[bodyView performSelector:@selector(forwardMethod)];

打印結(jié)果:

-[BodyView methodSignatureForSelector:]
執(zhí)行轉(zhuǎn)換對(duì)象的方法 == -[BodyView forwardInvocation:]
fatherView ---- forwardMethod
  • - (void)doesNotRecognizeSelector:(SEL)aSelector;處理接收器無(wú)法識(shí)別的消息,一般不要重寫(xiě)K嗜B叶浮!
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末仪壮,一起剝皮案震驚了整個(gè)濱河市憨颠,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌积锅,老刑警劉巖爽彤,帶你破解...
    沈念sama閱讀 206,839評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異缚陷,居然都是意外死亡适篙,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén)箫爷,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)嚷节,“玉大人,你說(shuō)我怎么就攤上這事蝶缀〉び鳎” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 153,116評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵翁都,是天一觀的道長(zhǎng)碍论。 經(jīng)常有香客問(wèn)我,道長(zhǎng)柄慰,這世上最難降的妖魔是什么鳍悠? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,371評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮坐搔,結(jié)果婚禮上藏研,老公的妹妹穿的比我還像新娘。我一直安慰自己概行,他們只是感情好蠢挡,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,384評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著凳忙,像睡著了一般业踏。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上涧卵,一...
    開(kāi)封第一講書(shū)人閱讀 49,111評(píng)論 1 285
  • 那天勤家,我揣著相機(jī)與錄音,去河邊找鬼柳恐。 笑死伐脖,一個(gè)胖子當(dāng)著我的面吹牛热幔,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播讼庇,決...
    沈念sama閱讀 38,416評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼绎巨,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了蠕啄?” 一聲冷哼從身側(cè)響起认烁,我...
    開(kāi)封第一講書(shū)人閱讀 37,053評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎介汹,沒(méi)想到半個(gè)月后却嗡,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,558評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡嘹承,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,007評(píng)論 2 325
  • 正文 我和宋清朗相戀三年窗价,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片叹卷。...
    茶點(diǎn)故事閱讀 38,117評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡撼港,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出骤竹,到底是詐尸還是另有隱情帝牡,我是刑警寧澤,帶...
    沈念sama閱讀 33,756評(píng)論 4 324
  • 正文 年R本政府宣布蒙揣,位于F島的核電站靶溜,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏懒震。R本人自食惡果不足惜罩息,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,324評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望个扰。 院中可真熱鬧瓷炮,春花似錦、人聲如沸递宅。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,315評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)办龄。三九已至烘绽,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間土榴,已是汗流浹背诀姚。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,539評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工响牛, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留玷禽,地道東北人赫段。 一個(gè)月前我還...
    沈念sama閱讀 45,578評(píng)論 2 355
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像矢赁,于是被迫代替她去往敵國(guó)和親糯笙。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,877評(píng)論 2 345

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