程序运行以后的内存管理。 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源码
内存中的五大区域.
类加载.
内存对齐