问题1:
简单解释 OC中 "成员变量" 与 "属性" ?
答案:
这个可参考之前写的 IOS底层(十): 类相关: 成员变量与属性
问题2:
简单解释 OC中 atomic / nonatomic, strong / copy / weak / assign?
答案:
这个可参考之前写的, LLVM部分可做了解
IOS底层(十一): 属性修饰分析
问题3:
定义一个 TestObj 类, 里面有一个实例方法, 一个类方法
.h 文件
#import <Foundation/Foundation.h>
@interface TestObj : NSObject
- (void)sayHello;
+ (void)sayHappy;
@end
.m 文件
#import "TestObj.h"
@implementation TestObj
// 实例方法
- (void)sayHello{
NSLog(@"Say : Hello!!!");
}
// 类方法
+ (void)sayHappy{
NSLog(@"Say : Happy!!!");
}
@end
问: 这个几个打印结果是什么样?(有返回1, 无返回0 即可)
TestObj *person = [TestObj alloc];
Class pClass = object_getClass(person);
classMethod_classToMetaclass(pClass);
void instanceMethod_classToMetaclass(Class pClass){
const char *className = class_getName(pClass);
Class metaClass = objc_getMetaClass(className);
Method method1 = class_getInstanceMethod(pClass, @selector(sayHello));
Method method2 = class_getInstanceMethod(metaClass, @selector(sayHello));
Method method3 = class_getInstanceMethod(pClass, @selector(sayHappy));
Method method4 = class_getInstanceMethod(metaClass, @selector(sayHappy));
NSLog(@"%s - %p-%p-%p-%p",__func__,method1,method2,method3,method4);
}
void classMethod_classToMetaclass(Class pClass){
const char *className = class_getName(pClass);
Class metaClass = objc_getMetaClass(className);
Method method1 = class_getClassMethod(pClass, @selector(sayHello));
Method method2 = class_getClassMethod(metaClass, @selector(sayHello));
Method method3 = class_getClassMethod(pClass, @selector(sayHappy));
Method method4 = class_getClassMethod(metaClass, @selector(sayHappy));
NSLog(@"%s-%p-%p-%p-%p",__func__,method1,method2,method3,method4);
}
答案:
打印一下:
instanceMethod_classToMetaclass - 0x1000081b0-0x0-0x0-0x100008148
classMethod_classToMetaclass-0x0-0x0-0x100008148-0x100008148
//后面指针可不同
所以第一个结果为 1 0 0 1
所以第二个结果为 0 0 1 1
解释:
建议先看下: IOS底层(九): 类相关: 类结构分析
首先要清楚实例方法
是存在对应类
bits中, 而类方法
存在元类
bits中
那么instanceMethod_classToMetaclass 返回
- 第一个, 在
当前类
中找实例方法 sayHello
, 没问题有返回值 - 第二个, 在
元类
中找实例方法 sayHello
, 没有0x0 - 第三个, 在
当前类
中找实例方法 sayHappy
, 没有0x0 - 第四个, 在
元类
中找实例方法 sayHappy
, 这个可能有点疑问, 看下源码底层, 其实底层是_class_getMethod(cls, sel);
, 没问题有返回值
Method class_getInstanceMethod(Class cls, SEL sel)
{
if (!cls || !sel) return nil;
// This deliberately avoids +initialize because it historically did so.
// This implementation is a bit weird because it's the only place that
// wants a Method instead of an IMP.
#warning fixme build and search caches
// Search method lists, try method resolver, etc.
lookUpImpOrForward(nil, sel, cls, LOOKUP_RESOLVER);
#warning fixme build and search caches
return _class_getMethod(cls, sel);
}
那么classMethod_classToMetaclass 返回
- 第一个, 在
当前类
中找类方法sayHello
, 没有0x0 - 第二个, 在
元类
中找类方法sayHello
, 没有0x0 - 第三个, 在
当前类
中找类方法sayHappy
, 没问题有返回值 - 第四个, 在
元类
中找实例方法sayHappy
, 这个可能有点疑问, 看下源码, 底层其实是class_getInstanceMethod
, 并且元类的话这里cls->getMeta()
返回元类本身, 没问题有返回值
Method class_getClassMethod(Class cls, SEL sel)
{
if (!cls || !sel) return nil;
return class_getInstanceMethod(cls->getMeta(), sel);
}
Class getMeta() {
if (isMetaClassMaybeUnrealized()) return (Class)this;
else return this->ISA();
}
问题4:
定义一个 TestObj 类 (有返回1, 无返回0 即可)
BOOL re1 = [(id)[NSObject class] isKindOfClass:[NSObject class]];
BOOL re2 = [(id)[NSObject class] isMemberOfClass:[NSObject class]];
BOOL re3 = [(id)[TestObj class] isKindOfClass:[TestObj class]];
BOOL re4 = [(id)[TestObj class] isMemberOfClass:[TestObj class]];
NSLog(@" re1 :%hhd\n re2 :%hhd\n re3 :%hhd\n re4 :%hhd\n",re1,re2,re3,re4);
BOOL re5 = [(id)[NSObject alloc] isKindOfClass:[NSObject class]];
BOOL re6 = [(id)[NSObject alloc] isMemberOfClass:[NSObject class]];
BOOL re7 = [(id)[TestObj alloc] isKindOfClass:[TestObj class]];
BOOL re8 = [(id)[TestObj alloc] isMemberOfClass:[TestObj class]];
NSLog(@" re5 :%hhd\n re6 :%hhd\n re7 :%hhd\n re8 :%hhd\n",re5,re6,re7,re8);
答案:
打印一下:
re1 :1
re2 :0
re3 :0
re4 :0
re5 :1
re6 :1
re7 :1
re8 :1
解题思路:
本题重点考察对 类
, 元类
中isa以及集成指向的理解, 同时要掌握 isKindOfClass
以及 isMemberOfClass
, 先看下方法源码以及走势图, 留意这里找的是 +
是类方法, -
是实例方法
// 类方法
+ (BOOL)isMemberOfClass:(Class)cls {
return self->ISA() == cls; // 判断当前类的元类是否与传入类相等
}
// 实例方法
- (BOOL)isMemberOfClass:(Class)cls {
return [self class] == cls; // 判断当前类是否与传入类相等
}
// 类方法
+ (BOOL)isKindOfClass:(Class)cls {
// 依次循环 判断 superclass
// 获取类的元类 vs 传入类
// 根元类 vs 传入类
// 根类 vs 传入类
// 如果相等返回 true, 不相等返回false
for (Class tcls = self->ISA(); tcls; tcls = tcls->getSuperclass()) {
if (tcls == cls) return YES;
}
return NO;
}
// 实例方法
- (BOOL)isKindOfClass:(Class)cls {
// 依次循环 判断 superclass
// 当前类 vs 传入类
// 父类 vs 传入类
// 根类 vs 传入类
// 根类 vs 传入类
// 如果相等返回 true, 不相等返回false
for (Class tcls = [self class]; tcls; tcls = tcls->superclass) {
if (tcls == cls) return YES;
}
return NO;
}
源码可以看出
isMemberOfClass
-
实例方法:
对象的类
与传入类
进行对比 -
类方法:
元类
与传入类
进行对比
isKindOfClass:
-
实例方法:
对象的类
→父类
→根类
→nil
与传入类
进行对比 -
类方法:
元类
→根元类
→根类
→nil
(依次找父类) 与传入类
进行对比
但实际上 isKindOfClass
运行时我们会发现, 它底层走了objc_opt_isKindOfClass
这个方法, 那我们看一下objc_opt_isKindOfClass

objc_opt_isKindOfClass(id obj, Class otherClass)
{
#if __OBJC2__
if (slowpath(!obj)) return NO;
// 获取isa,
// 如果obj 是对象,则isa是类,
// 如果obj是类,则isa是元类
Class cls = obj->getIsa();
if (fastpath(!cls->hasCustomCore())) {
// 如果是类: `对象的类` → `父类` → `根类` → `nil` 与 `传入类` 进行对比
// 如果是元类: `元类` → `根元类` → `根类` → `nil` (依次找父类) 与 `传入类` 进行对比
for (Class tcls = cls; tcls; tcls = tcls->getSuperclass()) {
if (tcls == otherClass) return YES;
}
return NO;
}
#endif
return ((BOOL(*)(id, SEL, Class))objc_msgSend)(obj, @selector(isKindOfClass:), otherClass);
}

// 类方法
-
re1 :
1
; 使用+ isKindOfClass
- NSObject
根元类
VS NSObject(传入类) --- 不相等 - NSObject
根类
VS NSObject(传入类) --- 相等
- NSObject
-
re2 :
0
; 使用+isMemberOfClass
- NSObject的元类即
根元类
VS NSObject(传入类) --- 不相等
- NSObject的元类即
-
re3 :
0
; 使用+ isKindOfClass
- TestObj
元类
VS TestObj(传入类) --- 不相等 - TestObj
元类
的父类根元类
VS TestObj(传入类) --- 不相等 - TestObj
根元类
的父类
即根类
VS TestObj(传入类) --- 不相等 - TestObj
根类
的父类nil VS TestObj(传入类) --- 不相等
- TestObj
-
re4 :
0
; 使用+isMemberOfClass
- TestObj的元类 VS TestObj (传入类) --- 不相等
// 实例方法
-
re5 :
1
; 使用- isKindOfClass
- NSObject对象的类即: NSObject VS NSObject (传入类) --- 相等
-
re6 :
1
; 使用-isMemberOfClass
- NSObject对象的类即: TestObj VS NSObject (传入类) --- 相等
-
re7 :
1
使用- isKindOfClass
- TestObj对象的类即: TestObj VS TestObj (传入类) --- 相等
-
re8 :
1
; 使用-isMemberOfClass
- TestObj对象的类即: TestObj VS TestObj (传入类) --- 相等
问题5:
类对象在内存中存几份(或者问: 类存在几份) ?
答案:
类信息在内存中只存在一份, 所以类对象只有一份
问题6:
objc_object 与 对象的关系 ?
答案:
继承关系
所有对象都以objc_object为模板继承过来的, 但真正底层是一个objc_object(C/C++)的结构体类型
问题7第一问: main中代码运行是否会报错
// SAStudent.h
#import "SAPerson.h"
@interface SAStudent : SAPerson
+ (void)sayHello;
@end
// SAStudent.m
#import "SAStudent.h"
@implementation SAStudent
+ (void)sayHello {
NSLog(@"SayHello : %s", __func__);
}
@end
// SAPerson.h
#import <Foundation/Foundation.h>
@interface SAPerson : NSObject
+ (void)sayHappy;
@end
// SAPerson.m
#import "SAPerson.h"
@implementation SAPerson
+ (void)sayHappy {
NSLog(@"SayHappy : %s", __func__);
}
@end
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#import "SAPerson.h"
#import "SAStudent.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
SAStudent *p = [SAStudent alloc];
[SAStudent sayHello];
[SAStudent sayHappy];
[SAStudent performSelector:@selector(sayEasy)];
}
return 0;
}
答案:
会报错

本题其实考察isa走位图(图片见问题4),
给定类方法, 类方法存在元类里面, 元类里面其实是以实例方法存储, 查找循序 子元类
→ 父元类
→ 根元类
→ 根类
→ nil
-
类方法sayHello: 子元类 SAStudent 存在, 正常打印
-
类方法sayHappy: 子元类 SAStudent 不存在, 去父元类查找存在, 正常打印
-
类方法sayEasy: 子元类 SAStudent 不存在, 去父元类, 根元类, 根类查找不存在 直接报错返回
其中
[SAStudent performSelector:@selector(sayEasy)]
=[SAStudent sayEasy]
问题7第二问: 第一问基础上多加一个分类 NSObject+NSObject_Cate.h, 分类里面有一个SayEasy方法, 内容如下是否会报错
#import <Foundation/Foundation.h>
@interface NSObject (NSObject_Cate)
+ (void)sayEasy;
@end
#import "NSObject+NSObject_Cate.h"
@implementation NSObject (NSObject_Cate)
+ (void)sayEasy {
NSLog(@"SayEasy : %s", __func__);
}
@end
答案:
不会报错

其实相当于问题一, 只是在根类NSObject
里面加个一个方法, 当找到根类时候发现有, 直接返回
问题8 :
如下例子, 请问回答打印结果情况(回答对应相等/不等即可)并解释原因
TestObj *obj1 = [TestObj alloc];
TestObj *obj2 = [obj1 init];
TestObj *obj3 = [obj1 init];
NSLog(@"%@ - %p - %p", obj1, obj1, &obj1);
NSLog(@"%@ - %p - %p", obj2, obj2, &obj2);
NSLog(@"%@ - %p - %p", obj3, obj3, &obj3);
答案:
首先了解下, 三个打印依次是打印 对象内容
, 对象指针指向的内存地址
, 指针地址
① %@
: 打印的是对象的内容
② %p → &p1
: 打印的是对象的指针地址
③ %p → p1
: 打印的是指针地址
那么
-
第一列: obj1, ob2, obj3 相等, 打印的是对象内容都是
TestObj
开辟的内存空间 -
第二列: obj1, obj2, obj3 相等, 留意下
init
是不会对指针进行操作的, 而%p打印的是对象指针
, obj1、obj2、 obj3, 三个都是指向同一片内存区域( [TestObj alloc]开辟的), 所以不变 -
第三列: &obj1, &ob2, &obj3 不相等, 打印的是指针地址, obj1、obj2、 obj3三个不同指针, 地址不同
打印结果:


拓展个知识点开头0x7
栈区, 开头0x6
堆区。指针在64位系统下为8个字节故依次相差8(其实也做了连续开辟, 在栈里面是连续指针, 所以依次相差8)。3 > 2 > 1是因为栈是从高地址往低地址存的。
(持续更新....)
网友评论