Runtime series
Layman Runtime (a): acquaintance
layman Runtime (2): The data structure
layman Runtime (III): messaging
layman Runtime (IV): The nature of super
layman Runtime (e): a particular application
layman Runtime (VI): interview questions related
Q: You know it isa pointer?
isa
Pointer is used to maintain a relationship between objects and classes, and to ensure that the object classes and ableisa
to find the corresponding methods, instance variables, attributes, pointers protocol;- Before arm64 architecture,
isa
it is an ordinary pointer, point directlyobjc_class
, storedClass
,Meta-Class
object memory address.instance
Objectisa
point toclass
the object,class
the objectisa
point tometa-class
the object; - Starting arm64 architecture for
isa
optimized into a common body (union
) structure, bit field used to store more information. The data memory 64 stores a lot of separate things, which is used to store 33class
,meta-class
the memory address information of the object. Through the operation bitisa
value& ISA_MASK
mask, to getclass
,meta-class
the memory address of the object; isa
Pointer information stored;isa
Pointers to.
Portal: layman Runtime (b): Data Structure
Q: differences and connections between objects and classes metaclass object.
class
,meta-class
Underlying structures areobjc_class
structures,objc_class
inherit fromobjc_object
, so it also hasisa
a pointer, so it is also an object;class
Information stored in the example method, member variables, properties, and other protocols,
meta-class
the method stores the class information;isa
Pointer andsuperclass
a pointer;- Base class
meta-class
ofsuperclass
a base classclass
,
decided on a property: when we call a class method, will passclass
aisa
pointer to findmeta-class
inmeta-class
find whether such methods, if not, then bymeta-class
thesuperclass
pointer step by step to find the fathermeta-class
, has been found base classmeta-class
if such methods have not found it, it will go to the base classclass
implementation example of the method of the same name.
Q: Runtime message mechanism, objc_msgSend method call flow.
Portal: layman Runtime (c): the message mechanism
OC
method call, in fact, it is converted to objc_msgSend()
the calling function (not included [super message]
). objc_msgSend()
The execution flow can be divided into three major stages: messaging, dynamic method resolution, message forwarding.
Q: What is the result of calling the init method print is? (Super)
@interface HTPerson : NSObject
@end
@interface HTStudent : HTPerson
@end
@implementation HTStudent
- (instancetype)init
{
if (self = [super init]) {
NSLog(@"[self class] = %@",[self class]);
NSLog(@"[super class] = %@",[super class]);
NSLog(@"[self superclass] = %@",[self superclass]);
NSLog(@"[super superclass] = %@",[super superclass]);
}
return self;
}
@end
[self class] = HTStudent
[super class] = HTStudent
[self superclass] = HTPerson
[super superclass] = HTPerson
class
和superclass
方法的实现在 NSObject 类中,可以看到它们的返回值取决于receiver
。
+ (Class)class {
return self;
}
- (Class)class {
return object_getClass(self);
}
+ (Class)superclass {
return self->superclass;
}
- (Class)superclass {
return [self class]->superclass;
}
[self class]
是从receiverClass
开始查找方法的实现,如果没有重写的情况,则会一直找到基类 NSObject,然后调用。
[super class]
是从receiverClass->superclass
开始查找方法的实现,如果没有重写的情况,则会一直找到基类 NSObject,然后调用。
由于receiver
相同,所以它们的返回值是一样的。
Q:如何防止“调用无法识别的方法导致应用程序崩溃”?
实现doseNotRecognizeSelector
方法。
Q:@synthesize 和 @dynamic
@synthesize :
为属性生成下划线成员变量,并且自动生成setter
和getter
方法的实现。以前 Xcode 还没这么智能的时候就要这么做。而现在默认我们写的属性,会自动进行@synthesize
。
有时候我们不希望它自动生成,而是在程序运行过程中再去决定该方法的实现,就可以使用@dynamic
。@dynamic:
是告诉编译器不用自动生成setter
和getter
的实现,不用自动生成成员变量,等到运行时再添加方法实现,但是它不会影响setter
和getter
方法的声明。- 动态运行时语言与编译时语言的区别:动态运行时语言将函数决议推迟到运行时,编译时语言在编译器进行函数决议。OC 是动态运行时语言。
Q:能否向编译后的类增加实例变量?能否向运行时动态创建的类增加实例变量?
- 不能向编译后的类增加实例变量。类的内存布局在编译时就已经确定,类的实例变量列表存储在
class_ro_t
结构体里,编译时就确定了内存大小无法修改,所以不能向编译后的类增加实例变量。 - 能向运行时动态创建的类增加实例变量。运行时动态创建的类只是通过
alloc
分配了类的内存空间,没有对类进行内存布局,内存布局是在类初始化过程中完成的,所以能向运行时动态创建的类增加实例变量。
需要注意的是,要在调用注册类
的方法之前去完成实例变量的添加,因为注册类的时候,类的结构就生成了。说白了就是class_addIvar()
函数不能给已经存在的类动态添加成员变量
// 动态创建一对类和元类(参数:父类,类名,额外的内存空间)
Class newClass = objc_allocateClassPair([NSObject class], "Person", 0);
// 动态添加成员变量
class_addIvar(newClass, "_age", 4, 1, @encode(int));
class_addIvar(newClass, "_name", sizeof(NSString *), log2(sizeof(NSString *)), @encode(NSString *));
// 注册一对类和元类(要在类注册之前添加成员变量)
objc_registerClassPair(newClass);
// 创建实例
id person = [[newClass alloc] init];
[person setValue:@"Lucy" forKey:@"name"];
[person setValue:@"20" forKey:@"age"];
NSLog(@"name:%@, age:%@", [person valueForKey:@"name"], [person valueForKey:@"age"]);
// 当类和它的子类的实例存在时,不能调用 objc_disposeClassPair(),否则会 Crash:Attempt to use unknown class 0x1005af5c0.
person = nil;
// 销毁一对类和元类
objc_disposeClassPair(newClass);
// name:Lucy, age:20
Q:你是否有使用过 performSelector: 方法?
使用场景:一个类在编译时没有这个方法,在运行的时候才产生了这个方法,这个时候要调用这个方法就要用到performSelector:
方法。
关于动态添加方法的实现可以查看:传送门:深入浅出 Runtime(三):消息机制
Q:以下打印结果是什么?(isKindOfClass & isMemberOfClass)
@interface Person : NSObject
@end
......
BOOL res1 = [[NSObject class] isKindOfClass:[NSObject class]];
BOOL res2 = [[NSObject class] isMemberOfClass:[NSObject class]];
BOOL res3 = [[Person class] isKindOfClass:[Person class]];
BOOL res4 = [[Person class] isMemberOfClass:[Person class]];
NSLog(@"%d,%d,%d,%d", res1, res2, res3, res4);
......
打印结果:1,0,0,0
以下是isMemberOfClass
和isKindOfClass
方法以及object_getClass()
函数的实现。
isMemberOfClass
方法是判断当前instance/class
对象的isa
指向是不是class/meta-class
对象类型;isKindOfClass
方法是判断当前instance/class
对象的isa
指向是不是class/meta-class
对象或者它的子类类型。
Obviously isKindOfClass
the larger scope. If the method is invoked with instance
an object, mass participation should be the class
object. If the method is invoked with class
an object, mass participation should be the meta-class
object. So res2
- res4
are zero. So why res1
is it 1?
Because of NSObject class
object isa
points to its meta-class
target, and it meta-class
is superclass
directed to its class
target, so it satisfies the isKindOfClass
determination conditions of the process.
+ (BOOL)isMemberOfClass:(Class)cls {
return object_getClass((id)self) == cls;
}
- (BOOL)isMemberOfClass:(Class)cls {
return [self class] == cls;
}
+ (BOOL)isKindOfClass:(Class)cls {
for (Class tcls = object_getClass((id)self); tcls; tcls = tcls->superclass) {
if (tcls == cls) return YES;
}
return NO;
}
- (BOOL)isKindOfClass:(Class)cls {
for (Class tcls = [self class]; tcls; tcls = tcls->superclass) {
if (tcls == cls) return YES;
}
return NO;
}
Class object_getClass(id obj)
{
if (obj) return obj->getIsa();
else return Nil;
}