iOS中 類方法和實例方法及self和super

一、關于類方法和實例方法:

1、類方法:Class Method 有時被稱為靜態(tài)方法,類方法可以獨立于實例對象而執(zhí)行。在使用類方法時要注意以下幾點:

  • 類方法以+開頭,相當于static,不能被類的實例調(diào)用,只能由類對象調(diào)用。
  • 類方法使用self時,self代表類本身即Class,所以類方內(nèi)可以直接調(diào)用類方法(別調(diào)用自己!死循環(huán)?。?,不能直接調(diào)用實例方法,但是可以通過創(chuàng)建實例對象來調(diào)用實例方法。
  • 類方法中不能訪問屬性和實例變量,只能訪問類對象,但是可以通過創(chuàng)建實例對象來訪問屬性,有點雞肋~。
@interface JRSecondViewController ()
{
    UIImageView * _imageView;
}
@property(strong,nonatomic)UIButton *btn;
@end

@implementation JRSecondViewController

+(void)creatSomeObject
{
    _imageView = [[UIImageView alloc] init];
    self.btn = [UIButton new];
    [self creatMethod];
    [self creatInstance];
}
+(void)creatMethod{ }

-(void)creatInstance{ }

@end

image
類方法總結
  • 類方法也叫靜態(tài)方法或工廠方法
  • 在聲明(.h)和實現(xiàn)(.m)中函數(shù)以 + 開頭
  • 在應用程序開始運行時一直駐于內(nèi)存,所以可直接通過類名進行引用該方法:[UIColor whiteColor]
  • 調(diào)用類方法速度很快,但會占用內(nèi)存,適合整個應用程序中頻繁調(diào)用的方法
  • 類方法一般用于實現(xiàn)一些工具方法,比如對某個對象進行擴展,或者實現(xiàn)單例等
  • 類方法內(nèi)部可以通過self調(diào)用自己的類方法
  • 類方法內(nèi)部不可以通過self來調(diào)用自己的實例方法,需要通過創(chuàng)建一個自己的實例對象來訪問自己的實例方法
  • 類方法內(nèi)部不可以通過self來訪問自己的屬性如self.iconImageArr
  • 在類方法中要訪問自己的屬性,必須要在類中實例化一個對象,然后再調(diào)用,但是都要調(diào)用實例屬性了,為什么使用實例方法呢?
  • 注意:靜態(tài)內(nèi)存是有限制的且是連續(xù)存放,過多占用會導致程序無法啟動

2、實例方法:必須由類的實例對象調(diào)用,可以訪問屬性,實例變量,同樣可以訪問類對象,使用限制相對于類方法較少。

一、關于self和super

總的來說:self會優(yōu)先調(diào)用本類中的方法,super會優(yōu)先調(diào)用父類方法。但是,self是指向本類的指針,是類的隱藏參數(shù),指向當前調(diào)用方法的對象(類對象或者實例對象),super卻不是指向父類的指針,只是一個編譯器標識符,其在運行時中與self相同,指向同一個消息接受者,只是self會優(yōu)先在當前類的methodLists中查找方法,而super則是優(yōu)先從父類中查找, 向super發(fā)送消息是一種調(diào)用繼承鏈上的超類定義的 方法實現(xiàn)的方法。

// 基類:
@interface BaseViewController : UIViewController

- (id)returnSomething;

@end

@implementation BaseViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    self.view.backgroundColor = [UIColor whiteColor];
    NSLog(@"super -- %@ , self -- %@", [super class], [self class]);
}

- (id)returnSomething
{
    return [UIView new];
}
@end

// 子類:
@interface JRSecondViewController : BaseViewController

@end

@implementation JRSecondViewController

- (instancetype)init
{
    self = [super init];
    if (self) {
        NSLog(@"self == %@", [self class]);
        NSLog(@"super == %@", [super class]);
    }
    return self;
}

- (void)viewDidLoad {
    [super viewDidLoad];
    // 會從當前類的方法列表中開始找,如果沒有,就從父類中再找;
    NSLog(@"viewDidLoad -> self == %@", [self returnSomething]);
    // 如果父類中只用方法定義而未實現(xiàn)則此處會報錯
    NSLog(@"viewDidLoad -> super == %@", [super returnSomething]);
}

-(id)returnSomething
{
    return [UIImageView new];
}
@end

// 外部調(diào)用
JRSecondViewController * secondVC = [JRSecondViewController new];
[self presentViewController:baseNavVC animated:YES completion:^{

}];

// 打印結果:
17:38:30.721835+0800  self == JRSecondViewController
17:38:30.722161+0800  super == JRSecondViewController
17:38:30.738893+0800  super -- JRSecondViewController , self -- JRSecondViewController
17:38:30.740765+0800  viewDidLoad -> self == <UIImageView: 0x7f9e5e507f30; frame = (0 0; 0 0); userInteractionEnabled = NO; layer = <CALayer: 0x600000029c00>>
17:38:30.741180+0800  viewDidLoad -> super == <UIView: 0x7f9e5e650520; frame = (0 0; 0 0); layer = <CALayer: 0x600000030be0>>

結果分析:

  • 可以看到首先調(diào)用JRSecondViewControllerinit方法,但是在其中[self class][super class]均打印的是JRSecondViewController類!原因:
    • 無論[self class]還是[super class],其接受消息的對象都是當前 JRSecondViewController 的實例對象。而不同的是,super是告訴編譯器,調(diào)用 class 這個方法時,要去父類的方法,而不是本類里的。
  • 然后到viewDidLoad方法中首先調(diào)用了[super viewDidLoad];去執(zhí)行父類的viewDidLoad方法,但是這里在父類的方法中打印的[self class][super class]同樣指向了JRSecondViewController 類?。。≡颍?
    • 還是上面的原因,調(diào)用[super viewDidLoad];方法,其接收消息的對象依然是JRSecondViewController的是實例對象,但是現(xiàn)在父類中查找viewDidLoad方法。同理在上面代碼的基礎上,在父類的returnSomething方法中打印[self class][super class]會是什么結果呢???

經(jīng)過上面的例子再回來看self和super的實現(xiàn)原理可能更加好理解:

  • self 調(diào)用方法事實際上是通過objc_msgSend(id _Nullable self, SEL _Nonnull op, ...)函數(shù)進行消息的發(fā)送,其中第一個參數(shù)是消息接收者,第二個參數(shù)op是調(diào)用的具體類的方法的selector,后面是 selector 方法的可變參數(shù)。如上例所示[self returnSomething]實際上是id _Nullable objc_msgSend(self, @selector(returnSomething))returnSomething方法會從[self class]類中查找。

  • super調(diào)用方法事實際上是通過id _Nullable objc_msgSendSuper(struct objc_super * _Nonnull super, SEL _Nonnull op, ...)函數(shù)進行消息的發(fā)送,但是第一個參數(shù)是一個objc_super結構體。

struct objc_super {
    __unsafe_unretained _Nonnull id receiver;
    __unsafe_unretained _Nonnull Class super_class;
};

  • 此時這個結構體的第一個成員變量receiver就是子類,和 objc_msgSend 中的self相同。而第二個成員變量super_class就是指父類,調(diào)用 objc_msgSendSuper 的方法時會將這個結構體和returnSomething的selector傳遞過去。

  • 在結構體函數(shù)里面做的事情類似這樣:從objc_super結構體指向的super_class的方法列表開始找 returnSomething的selector,找到后再用objc_super->receiver去調(diào)用這個selector。找不到就會報錯。

這樣結合上述例子和self和super的原理就會很容易明白為什么[self class][super class]輸出結果會是一樣的,同時在BaseViewControllerviewDidLoad[self class][super class]輸出都是子類類對象了

實例方法總結:
  • 實例方法也叫動態(tài)方法或對象方法
  • 在聲明(.h)和實現(xiàn)(.m)中函數(shù)以 - 開頭
  • 需要創(chuàng)建類的實例后才能引用該方法:[[NSUserDefaults standardUserDefaults] objectForKey:@"..."]
  • 實例方法調(diào)用動態(tài)分配內(nèi)存,調(diào)用完成后會釋放內(nèi)存,節(jié)省內(nèi)存,但調(diào)用速度較類方法慢
  • 同樣的,實例方法內(nèi)部可以通過self調(diào)用實例方法
    實例方法內(nèi)部不可以通過self調(diào)用類方法,可以使用[self class]來調(diào)用類方法

參考鏈接

iOS中關于類方法和實例方法及self和super

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。