程序運行以後的內存管理。 OC具備動態特性,即運行時才檢查對象類型和方法實現。html
hit / miss / add。緩存
緩存方法列表 -> 對象方法列表 -> 父類的方法列表 -> 消息轉發機制 -> 拋異常bash
/*
傳入 instance 對象,返回 class 對象
傳入 class 對象,返回 meta-class 對象
傳入 meta-class 對象,返回 NSObject 基類的 meta-class 對象
*/
Class object_getClass(id obj)
//只能返回類對象,不管調用幾回
- (Class)class、 + (Class)class
//未測試,拿不到meta類的類名,NSStringFromClass又要調用class方法,又回到普通
Class objc_getClass(const char *aClassName)
複製代碼
內部實現,見Runtime源碼數據結構
調用對象
都是self,super的意思是從父類的方法列表中找
method(找不到,就去父類的父類中找)。[super method]
方法中,調用了[self amethod]
這裏self的調用先從obj本身的方法列表中找
amethod。由於都是調用的NSObect的-(Class)class
方法,實現以下:app
return objc_getClass(self);
複製代碼
不會,由於,不管self是誰,都不會去引用UIView。 下面的代碼中,dom
drawFollowing:
實例方法中的隱藏參數。- (void)drawFollowing:(CGPoint)previousPoint
{
CGRect frame = CGRectZero;
UIImageView *aview = [self createImageView:frame];
[UIView animateWithDuration:2
delay:0
usingSpringWithDamping:1
initialSpringVelocity:0
options:UIViewAnimationOptionCurveEaseInOut
animations:^{
//這裏是2秒後的狀態,因此rect.size.width=0沒問題
aimageView.alpha = 0.7;
CGRect rect = imageView.frame;
rect.size.width = 0;
rect.size.height = 0;
//self
imageView.transform=CGAffineTransformMakeRotation(M_PI/[self getRandomNumber:1 to:6]);
imageView.frame = rect;
}
}
];
}
複製代碼
對類簇的理解,只停留在諸如NSArray這種集合類上。ide
@dynamic
,不生成實例變量、getter/setter。 @synthesize
,自動合成getter/setter,實例變量。iOS6以後,編譯器有了,屬性自動合成,無需此句了。函數
\ | 父類屬性 | dynamic | category聲明的屬性 | @implemetaion A{NSString * aInstance} |
---|---|---|---|---|
Ivar* | 不含 | 不含 | 不含 | 包含 |
objc_property_t * | 不含 | 包含 | 包含 | 不含 |
category添加的屬性和方法,編譯階段已經鏈接好了,運行時,加載Class對象的時候,所有加入屬性列表
和方法列表
中,不影響方法查找流程。oop
category添加的同名方法會覆蓋原類中的方法,不管是否import category。
多個category,+load方法不會分類覆蓋,而是按照project.pbxproj文件中PBXSourcesBuildPhase的順序依次執行。
多個category,+initialize方法,只會有一個生效,project.pbxproj文件中PBXSourcesBuildPhase中最後的category。
多個category,實例方法,會覆蓋,只會有一個生效,是誰由編譯器決定。
@dynamic pages;
- (void)setPages:(NSArray *)pages {
objc_setAssociatedObject(self, _cmd, pages, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (NSArray *)pages {
return objc_getAssociatedObject(self, @selector(pages));
}
複製代碼
- (void)setBb:(NSInteger)bb {
objc_setAssociatedObject(self, @selector(bb), @(bb), OBJC_ASSOCIATION_RETAIN);
}
- (long)bb {
NSLog(@"Current method: %@ %@",[self class],NSStringFromSelector(_cmd));
return [objc_getAssociatedObject(self, _cmd) integerValue];
}
複製代碼
- (void)setPageView:(MyPageView *) pageView{
[self willChangeValueForKey:@"pageView"];
objc_setAssociatedObject(self, @selector(pageView),
pageView,
OBJC_ASSOCIATION_RETAIN_NONATOMIC);
[self didChangeValueForKey:@"pageView"];
}
複製代碼
#import "objc/runtime.h"
複製代碼
unsigned int count = 0;
Ivar *ivars = class_copyIvarList([self class], &count);
for (int i=0; i < count; i++) {
Ivar const ivar = ivars[i];
//獲取屬性名
NSString *key = [NSString stringWithUTF8String:ivar_getName(ivar)];
//獲取屬性值,小心crash
id value = [self valueForKey:key];
}
if(ivars){
free(ivars);
}
複製代碼
對方法的反射,是針對.m的。 注意,方法反射,只能拿到-
方法(靜態方法拿不到),拿不到父類的方法,能夠拿到分類方法。
每一個線程都會自動建立本身最外層的autorealsepool。 autoreleasepool能夠嵌套。
沒有手動建立autoreleasepool的狀況下,等到線程執行
下一次事件循環
(如for循環總體結束後、線程執行完等)時,纔去清空再也不使用的對象。 對於頻繁的文件操做、for循環中的圖片
等操做,使用單獨的autoreleasepool可能會下降memory peak
。(eoc-34)
注意:這裏提到了事件循環,不必定是RunLoop,只是一個生命週期的概念?由於子線程不會自動建立RunLoop。
緩存方法列表 -> 對象的方法列表 -> 父類的方法列表 -> 動態方法解析 -> 消息轉發。 應用場景,除了捕獲Crash,暫時沒找到,並且捕獲Crash有更好的方式。
消息轉發有三個階段
另外一個類的某方法
能夠不止一個類的某些方法
//本身處理
+ (BOOL)resolveInstanceMethod:(SEL)sel {} (實例方法)
+ (BOOL)resolveClassMethod:(SEL)sel {} (類方法)
//轉發給其餘類的其餘方法
- (id)forwardingTargetForSelector:(SEL)aSelector {}
//
//第一個要求返回一個方法簽名,第二個方法轉發具體的實現。兩者相互依賴,只有返回了正確的方法簽名,纔會執行第二個方法。
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {}
- (void)forwardInvocation:(NSInvocation *)anInvocation {}
// ViewController.m 中
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
//下面幾行是告訴編譯器,不要對特定類型(未聲明方法)報警
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wundeclared-selector"
if (aSelector == @selector(myTestPrint:)) {
#pragma clang diagnostic pop
return [NSMethodSignature signatureWithObjCTypes:"v@:@"];
}
return [super methodSignatureForSelector:aSelector];
}
- (void)forwardInvocation:(NSInvocation *)anInvocation {
Person *person = [Person new];
Animal *animal = [Animal new];
if ([person respondsToSelector:anInvocation.selector]) {
[anInvocation invokeWithTarget:person];
}
if ([animal respondsToSelector:anInvocation.selector]) {
[anInvocation invokeWithTarget:animal];
}
}
複製代碼
//在NSMethodSignature.h中
+ (nullable NSMethodSignature *)signatureWithObjCTypes:(const char *)types; //1
//在NSObject.h中
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector OBJC_SWIFT_UNAVAILABLE(""); //2
+ (NSMethodSignature *)instanceMethodSignatureForSelector:(SEL)aSelector OBJC_SWIFT_UNAVAILABLE(""); //3
複製代碼
NSMethodSignature是方法簽名,是用來記錄返回值類型和參數類型的一個對象。 2和3兩個方法是根據SEL來構造NSMethodSignature 1是根據ObjCTypes來構造NSMethodSignature ObjCTypes是一個是字符串數組,該數組包含了方法的類型編碼
- (void)saySth:(Something *)sth;
複製代碼
其ObjcTypes就是 "v@:@",有兩種方式獲取該字符串
@encode()
計算。( NSLog(@"%s",@encode(BOOL))
的結果爲B )舉例,消息轉發中
[sbody saySth:sth];
//-> void objc_msgSend(sbody, @selector(saySth:), sth);
複製代碼
v@:@
各符號含義以下,
v
指代void,(連起來,是 返回值void)@
指代對象,(連起來,是 消息的接受者,即sbody):
指代SEL@
指代對象,(連起來,是 消息的參數,即sth)引用計數 isa管理對象的引用計數
SideTables,全局變量,容量爲64的數組,數組元素是SideTable 一個obj對應一個SideTable。 一個SideTable對應多個Obj。
這裏有一個二次hash,緣由:SideTables最大是64。能夠理解爲大小表,下降索引量,另外聯繫到hash衝突是用index+1的方式,而不是拉鍊法,也多是爲了下降
hash衝突
的可能性。 第一次,是obj->SideTables 第二次,是obj->計數表、弱引用表
數據結構, 見Runtime源碼
內存中的五大區域.
類加載.
內存對齊