In layman's language Runtime (six): the relevant interview questions

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?

  • isaPointer is used to maintain a relationship between objects and classes, and to ensure that the object classes and able isato find the corresponding methods, instance variables, attributes, pointers protocol;
  • Before arm64 architecture, isait is an ordinary pointer, point directly objc_class, stored Class, Meta-Classobject memory address. instanceObject isapoint to classthe object, classthe object isapoint to meta-classthe object;
  • Starting arm64 architecture for isaoptimized 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 33 class, meta-classthe memory address information of the object. Through the operation bit isavalue & ISA_MASKmask, to get class, meta-classthe memory address of the object;
  • isaPointer information stored;
  • isaPointers to.
    Portal: layman Runtime (b): Data Structure

Q: differences and connections between objects and classes metaclass object.

  • class, meta-classUnderlying structures are objc_classstructures, objc_classinherit from objc_object, so it also has isaa pointer, so it is also an object;
  • classInformation stored in the example method, member variables, properties, and other protocols,
    meta-classthe method stores the class information;
  • isaPointer and superclassa pointer;
  • Base class meta-classof superclassa base class class,
    decided on a property: when we call a class method, will pass classa isapointer to find meta-classin meta-classfind whether such methods, if not, then by meta-classthe superclasspointer step by step to find the father meta-class, has been found base class meta-classif such methods have not found it, it will go to the base class classimplementation example of the method of the same name.
    isa pointer to the superclass

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

classsuperclass方法的实现在 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 :为属性生成下划线成员变量,并且自动生成settergetter方法的实现。以前 Xcode 还没这么智能的时候就要这么做。而现在默认我们写的属性,会自动进行@synthesize
    有时候我们不希望它自动生成,而是在程序运行过程中再去决定该方法的实现,就可以使用@dynamic
  • @dynamic:是告诉编译器不用自动生成settergetter的实现,不用自动生成成员变量,等到运行时再添加方法实现,但是它不会影响settergetter方法的声明。
  • 动态运行时语言与编译时语言的区别:动态运行时语言将函数决议推迟到运行时,编译时语言在编译器进行函数决议。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
以下是isMemberOfClassisKindOfClass方法以及object_getClass()函数的实现。

  • isMemberOfClass方法是判断当前instance/class对象的isa指向是不是class/meta-class对象类型;
  • isKindOfClass方法是判断当前instance/class对象的isa指向是不是class/meta-class对象或者它的子类类型。

Obviously isKindOfClassthe larger scope. If the method is invoked with instancean object, mass participation should be the classobject. If the method is invoked with classan object, mass participation should be the meta-classobject. So res2- res4are zero. So why res1is it 1?
Because of NSObject classobject isapoints to its meta-classtarget, and it meta-classis superclassdirected to its classtarget, so it satisfies the isKindOfClassdetermination 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;
}
Published 12 original articles · won praise 0 · Views 190

Guess you like

Origin blog.csdn.net/weixin_42350379/article/details/104509271