一、關于類方法和實例方法:
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
類方法總結
- 類方法也叫靜態(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)用
JRSecondViewController
的init
方法,但是在其中[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]
會是什么結果呢???
- 還是上面的原因,調(diào)用
經(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]
輸出結果會是一樣的,同時在BaseViewController
的viewDidLoad
中[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)用類方法