系列:
【靠谱程序员#6】《招聘一个靠谱的程序员》个人解答(end)
11. @synthesize和@dynamic分别有什么作用?
答:
说明:
在使用@property声明一个属性@property(nonatomic, copy) NSString *name;的时候,如果没有写@synthesize和@dynamic,且没有手动生成getter和setter,那么编译器会默认将其处理成@synthesize声明了一个成员变量:@synthesize name = _name的形式,同时自动生成getter和setter方法。
所以当使用@property声明一个属性,且同时没有手动生成getter和setter方法的时候,编译器会在编译期间默认帮我们使用@synthesize声明一个成员变量,同时生成getter和setter方法。如果你手动实现了getter方法,且手动实现了setter方法(readonly实现一个getter即可),那么你需要加上@synthesize手动生成一个成员变量才能通过编译。
如果你使用@dynamic name;则告诉编译器不用去生成getter和setter方法,用户会自己去生成,同时也没有成员变量生成,但是假如用户没有去生成,编译不会有问题,但是如果使用到了getter(NSString *name = xxx.name)和setter(xxx.name = @""),运行过程会报错。
结论:
- 前提:使用@property声明一个属性
- @synthesize的作用:告诉编译器自动生成getter和setter方法(readonly只生成getter)
- @dynamic的作用:告诉编译器不用去自动生成getter和setter方法(readonly为getter),用户自己会去实现。
12. ARC下,不显式指定任何属性关键字时,默认的关键字都有哪些?
答:
1)、原子性--atomic
2)、读写权限--readwrite
3)、内存管理--基础数据类型(assign),对象(strong)
Reference:
All the attributes information together:
atomic //default
nonatomic
strong=retain //default
weak
retain
assign //default
unsafe_unretained
copy
readonly
readwrite //default
Below is the detailed article link where you can find above mentioned all attributes, that will definitely help you. Many thanks to all the people who give best answers here!!
Variable property attributes or Modifiers in iOS
1.strong (iOS4 = retain )
it says "keep this in the heap until I don't point to it anymore" in other words " I'am the owner, you cannot dealloc this before aim fine with that same as retain" You use strong only if you need to retain the object. By default all instance variables and local variables are strong pointers. We generally use strong for UIViewControllers (UI item's parents) strong is used with ARC and it basically helps you , by not having to worry about the retain count of an object. ARC automatically releases it for you when you are done with it.Using the keyword strong means that you own the object. Example:
@property (strong, nonatomic) ViewController *viewController;
@synthesize viewController;
2.weak
it says "keep this as long as someone else points to it strongly" the same thing as assign, no retain or release A "weak" reference is a reference that you do not retain. We generally use weak for IBOutlets (UIViewController's Childs).This works because the child object only needs to exist as long as the parent object does. a weak reference is a reference that does not protect the referenced object from collection by a garbage collector. Weak is essentially assign, a unretained property. Except the when the object is deallocated the weak pointer is automatically set to nil Example :
@property (weak, nonatomic) IBOutlet UIButton *myButton;
@synthesize myButton;
Strong & Weak Explanation, Thanks to BJ Homer:
Imagine our object is a dog, and that the dog wants to run away (be deallocated).
Strong pointers are like a leash on the dog. As long as you have the leash attached to the dog, the dog will not run away. If five people attach their leash to one dog, (five strong pointers to one object), then the dog will not run away until all five leashes are detached.
Weak pointers, on the other hand, are like little kids pointing at the dog and saying "Look! A dog!" As long as the dog is still on the leash, the little kids can still see the dog, and they'll still point to it. As soon as all the leashes are detached, though, the dog runs away no matter how many little kids are pointing to it.
As soon as the last strong pointer (leash) no longer points to an object, the object will be deallocated, and all weak pointers will be zeroed out. When we use weak?
The only time you would want to use weak, is if you wanted to avoid retain cycles (e.g. the parent retains the child and the child retains the parent so neither is ever released).
3.retain = strong
it is retained, old value is released and it is assigned retain specifies the new value should be sent retain on assignment and the old value sent -release retain is the same as strong. apple says if you write retain it will auto converted/work like strong only. methods like "alloc" include an implicit "retain" Example:
@property (nonatomic, retain) NSString *name;
@synthesize name;
4.assign
assign is the default and simply performs a variable assignment assign is a property attribute that tells the compiler how to synthesize the property's setter implementation I would use assign for C primitive properties and weak for weak references to Objective-C objects. Example:
@property (nonatomic, assign) NSString *address;
@synthesize address;
Links:
Objective-C ARC: strong vs retain and weak vs assign
Variable property attributes or Modifiers in iOS
13. 用@property声明的NSString(或NSArray,NSDictionary)经常使用copy关键字,为什么?如果改用strong关键字,可能造成什么问题?
答:
1)、因为父类指针可以指向子类对象(子类给父类赋值),使用 copy 的目的是为了让本对象的属性不受外界影响,使用 copy 无论给我传入是一个可变对象还是不可对象,我本身持有的就是一个不可变的副本.
2)、如果我们使用是 strong ,那么这个属性就有可能指向一个可变对象,如果这个可变对象在外部被修改了,那么会影响该属性.
tips:
子类给父类赋值是安全的,父类给子类赋值是不安全的。
// Person.h
@interface Person : NSObject
@property (nonatomic, copy) NSString *name;
@end
// Man.h
@interface Man : Person
@property (nonatomic, assign) NSUInteger age;
@end
// Test.m
- (void)test1 {
Man *m = [Person new];
NSLog(@"%@",m.className); //Person
m.age = 12; //运行Crash
}
- (void)test2 {
Person *p = [Man new];
NSLog(@"%@",p.className); //Man
//p.age = 12; //p没有设置age的方法,编译报错
}
举例说明:
定义一个以 strong 修饰的 array:
@property (nonatomic ,readwrite, strong) NSArray *array;
然后进行下面的操作:
NSArray *array = @[ @1, @2, @3, @4 ];
NSMutableArray *mutableArray = [NSMutableArray arrayWithArray:array];
self.array = mutableArray;
[mutableArray removeAllObjects];;
NSLog(@"%@",self.array);
[mutableArray addObjectsFromArray:array];
self.array = [mutableArray copy];
[mutableArray removeAllObjects];;
NSLog(@"%@",self.array);
打印结果如下所示:
2015-09-27 19:10:32.523 CYLArrayCopyDmo[10681:713670] (
)
2015-09-27 19:10:32.524 CYLArrayCopyDmo[10681:713670] (
1,
2,
3,
4
)
为了理解这种做法,首先要知道,两种情况:
对非集合类对象的 copy 与 mutableCopy 操作;
对集合类对象的 copy 与 mutableCopy 操作。
1. 对非集合类对象的copy操作:
- 在非集合类对象中:对 immutable 对象进行 copy 操作,是指针复制,mutableCopy 操作时内容复制;
- 对 mutable 对象进行 copy 和 mutableCopy 都是内容复制。
用代码简单表示如下:
[immutableObject copy] // 浅复制
[immutableObject mutableCopy] //深复制
[mutableObject copy] //深复制
[mutableObject mutableCopy] //深复制
比如以下代码:
NSMutableString *string = [NSMutableString stringWithString:@"origin"];//copy
NSString *stringCopy = [string copy];
查看内存,会发现 string、stringCopy 内存地址都不一样,说明此时都是做内容拷贝、深拷贝。即使你进行如下操作:
[string appendString:@"origion!"];
stringCopy 的值也不会因此改变,但是如果不使用 copy,stringCopy 的值就会被改变。 集合类对象以此类推。 所以,
总结:用 @property 声明 NSString、NSArray、NSDictionary 经常使用 copy 关键字,是因为他们有对应的可变类型:NSMutableString、NSMutableArray、NSMutableDictionary,他们之间可能进行赋值操作,为确保对象中的字符串值不会无意间变动,应该在设置新属性值时拷贝一份。
2、集合类对象的copy与mutableCopy
集合类对象是指 NSArray、NSDictionary、NSSet ... 之类的对象。下面先看集合类immutable对象使用 copy 和 mutableCopy 的一个例子:
NSArray *array = @[@[@"a", @"b"], @[@"c", @"d"]];
NSArray *copyArray = [array copy];
NSMutableArray *mCopyArray = [array mutableCopy];
查看内容,可以看到 copyArray 和 array 的地址是一样的,而 mCopyArray 和 array 的地址是不同的。说明 copy 操作进行了指针拷贝,mutableCopy 进行了内容拷贝。但需要强调的是:此处的内容拷贝,仅仅是拷贝 array 这个对象,array 集合内部的元素仍然是指针拷贝。这和上面的非集合 immutable 对象的拷贝还是挺相似的,那么mutable对象的拷贝会不会类似呢?我们继续往下,看 mutable 对象拷贝的例子:
NSMutableArray *array = [NSMutableArray arrayWithObjects:[NSMutableString stringWithString:@"a"],@"b",@"c",nil];
NSArray *copyArray = [array copy];
NSMutableArray *mCopyArray = [array mutableCopy];
查看内存,如我们所料,copyArray、mCopyArray和 array 的内存地址都不一样,说明 copyArray、mCopyArray 都对 array 进行了内容拷贝。同样,我们可以得出结论:
在集合类对象中,对 immutable 对象进行 copy,是指针复制, mutableCopy 是内容复制;对 mutable 对象进行 copy 和 mutableCopy 都是内容复制。但是:集合对象的内容复制仅限于对象本身,对象元素仍然是指针复制。
用代码简单表示如下:
[immutableObject copy] // 浅复制
[immutableObject mutableCopy] //单层深复制
[mutableObject copy] //单层深复制
[mutableObject mutableCopy] //单层深复制
这个代码结论和非集合类的非常相似。
扩展结论:
1)、对于非集合对象,对于不可变对象使用copy是浅复制(指针复制,典型的是[NSString copy]),其他所有情况(可变对象/mutableCopy)都是深复制;
2)、对于集合对象,对于不可变对象使用copy是浅复制,其他情况都是深复制,但是深复制仅是对于集合对象本身,对于集合对象的元素,仍然是浅复制。
参考链接:
14. @synthesize合成实例变量的规则是什么?假如property名为foo,存在一个名为_foo的实例变量,那么还会自动合成新变量么?
答:
如果使用了属性的话,那么编译器就会自动编写访问属性所需的方法,此过程叫做“自动合成”( auto synthesis)。需要强调的是,这个过程由编译器在编译期执行,所以编辑器里看不到这些“合成方法” (synthesized method)的源代码。除了生成方法代码之外,编译器还要自动向类中添加适当类型的实例变量,并且在属性名前面加下划线,以此作为实例变量的名字。
@interface CYLPerson : NSObject
@property NSString *firstName;
@property NSString *lastName;
@end
在上例中,会生成两个实例变量,其名称分别为 _firstName 与 _lastName。也可以在类的实现代码里通过 @synthesize 语法来指定实例变量的名字:
@implementation CYLPerson
@synthesize firstName = _myFirstName;
@synthesize lastName = _myLastName;
@end
上述语法会将生成的实例变量命名为 _myFirstName 与 _myLastName ,而不再使用默认的名字。一般情况下无须修改默认的实例变量名,但是如果你不喜欢以下划线来命名实例变量,那么可以用这个办法将其改为自己想要的名字。笔者还是推荐使用默认的命名方案,因为如果所有人都坚持这套方案,那么写出来的代码大家都能看得懂。
总结下 @synthesize 合成实例变量的规则,有以下几点:
-
如果指定了成员变量的名称,会生成一个指定的名称的成员变量,
-
如果这个成员已经存在了就不再生成了.
-
如果是 @synthesize foo;还会生成一个名称为foo的成员变量。也就是说:如果没有指定成员变量的名称会自动生成一个属性同名的成员变量, 如果是 @synthesize foo = _foo; 就不会生成成员变量了.
假如 property 名为 foo,存在一个名为 _foo 的实例变量,那么还会自动合成新变量么? 不会。如下图:

15. 在有了自动合成属性实例变量之后,@synthesize还有哪些使用场景?
答:
回答这个问题前,我们要搞清楚一个问题,什么情况下不会autosynthesis(自动合成)?
- 同时重写了 setter 和 getter 时
- 重写了只读属性的 getter 时
- 使用了 @dynamic 时
- 在 @protocol 中定义的所有属性
- 在 category 中定义的所有属性
- 重载的属性 当你在子类中重载了父类中的属性,你必须 使用 @synthesize 来手动合成ivar。
除了后三条,对其他几个我们可以总结出一个规律:
当你想手动管理 @property 的所有内容时,你就会尝试通过实现 @property 的所有“存取
方法”(the accessor methods)或者使用 @dynamic 来达到这个目的,这时编译器就会
认为你打算手动管理 @property,于是编译器就禁用了 autosynthesis(自动合成)。
因为有了 autosynthesis(自动合成),大部分开发者已经习惯不去手动定义ivar,而是依赖于 autosynthesis(自动合成),但是一旦你需要使用ivar,而 autosynthesis(自动合成)又失效了,如果不去手动定义ivar,那么你就得借助 @synthesize 来手动合成 ivar。
其实,@synthesize 语法还有一个应用场景,但是不太建议大家使用:
可以在类的实现代码里通过 @synthesize 语法来指定实例变量的名字:
@implementation CYLPerson
@synthesize firstName = _myFirstName;
@synthesize lastName = _myLastName;
@end
上述语法会将生成的实例变量命名为 _myFirstName 与 _myLastName,而不再使用默认的名字。一般情况下无须修改默认的实例变量名,但是如果你不喜欢以下划线来命名实例变量,那么可以用这个办法将其改为自己想要的名字。笔者还是推荐使用默认的命名方案,因为如果所有人都坚持这套方案,那么写出来的代码大家都能看得懂。
举例说明:应用场景:
//
// .m文件
// 打开注释中的任意一行,就可编译成功
@import Foundation;
@interface CYLObject : NSObject
@property (nonatomic, copy) NSString *title;
@end
@implementation CYLObject {
// NSString *_title;
}
//@synthesize title = _title;
- (instancetype)init
{
self = [super init];
if (self) {
_title = @"title";
}
return self;
}
- (NSString *)title {
return _title;
}
- (void)setTitle:(NSString *)title {
_title = [title copy];
}
@end
注释掉那两行,编译不通过,放开其中一个,则编译运行没问题
当你同时重写了 setter 和 getter 时,系统就不会生成 ivar(实例变量/成员变量)。这时候有两种选择:
要么:手动创建 ivar
要么:使用@synthesize foo = _foo;,关联 @property 与 ivar。
更多信息,请戳-> When should I use @synthesize explicitly?
16. objc中向一个nil对象发送消息将会发生什么?
答:
在 Objective-C 中向 nil 发送消息是完全有效的——只是在运行时不会有任何作用:
1.如果一个方法返回值是一个对象,那么发送给nil的消息将返回0(nil)。例如:
Person * motherInlaw = [[aPerson spouse] mother];
如果 spouse 对象为 nil,那么发送给 nil 的消息 mother 也将返回 nil。 ### 2. 如果方法返回值为指针类型,其指针大小为小于或者等于sizeof(void*),float,double,long double 或者 long long 的整型标量,发送给 nil 的消息将返回0。
3. 如果方法返回值为结构体,发送给 nil 的消息将返回0。结构体中各个字段的值将都是0。
4. 如果方法的返回值不是上述提到的几种情况,那么发送给 nil 的消息的返回值将是未定义的。
具体原因如下:
objc是动态语言,每个方法在运行时会被动态转为消息发送,即:objc_msgSend(receiver, selector)。 那么,为了方便理解这个内容,还是贴一个objc的源代码:
// runtime.h(类在runtime中的定义)
// http://weibo.com/luohanchenyilong/
// https://github.com/ChenYilong
struct objc_class {
Class isa OBJC_ISA_AVAILABILITY; //isa指针指向Meta Class,因为Objc的类的本身也是一个Object,为了处理这个关系,runtime就创造了Meta Class,当给类发送[NSObject alloc]这样消息时,实际上是把这个消息发给了Class Object
#if !__OBJC2__
Class super_class OBJC2_UNAVAILABLE; // 父类
const char *name OBJC2_UNAVAILABLE; // 类名
long version OBJC2_UNAVAILABLE; // 类的版本信息,默认为0
long info OBJC2_UNAVAILABLE; // 类信息,供运行期使用的一些位标识
long instance_size OBJC2_UNAVAILABLE; // 该类的实例变量大小
struct objc_ivar_list *ivars OBJC2_UNAVAILABLE; // 该类的成员变量链表
struct objc_method_list **methodLists OBJC2_UNAVAILABLE; // 方法定义的链表
struct objc_cache *cache OBJC2_UNAVAILABLE; // 方法缓存,对象接到一个消息会根据isa指针查找消息对象,这时会在method Lists中遍历,如果cache了,常用的方法调用时就能够提高调用的效率。
struct objc_protocol_list *protocols OBJC2_UNAVAILABLE; // 协议链表
#endif
} OBJC2_UNAVAILABLE;
objc在向一个对象发送消息时,runtime库会根据对象的isa指针找到该对象实际所属的类,然后在该类中的方法列表以及其父类方法列表中寻找方法运行,然后在发送消息的时候,objc_msgSend方法不会返回值,所谓的返回内容都是具体调用时执行的。
那么,回到本题,如果向一个nil对象发送消息,首先在寻找对象的isa指针时就是0地址返回了,所以不会出现任何错误。
17. objc中向一个对象发送消息[obj foo]和objc_msgSend()函数之间有什么关系?
答:
具体原因同上题:该方法编译之后就是objc_msgSend()函数调用.
我们用 clang 分析下,clang 提供一个命令,可以将Objective-C的源码改写成C++语言,借此可以研究下[obj foo]和objc_msgSend()函数之间有什么关系。
以下面的代码为例,由于 clang 后的代码达到了10万多行,为了便于区分,添加了一个叫 iOSinit 方法,
//
// main.m
// http://weibo.com/luohanchenyilong/
// https://github.com/ChenYilong
// Copyright (c) 2015年 微博@iOS程序犭袁. All rights reserved.
//
#import "CYLTest.h"
int main(int argc, char * argv[]) {
@autoreleasepool {
CYLTest *test = [[CYLTest alloc] init];
[test performSelector:(@selector(iOSinit))];
return 0;
}
}
在终端中输入
clang -rewrite-objc main.m
就可以生成一个main.cpp的文件,在最低端(10万4千行左右)

我们可以看到大概是这样的:
((void ()(id, SEL))(void )objc_msgSend)((id)obj, sel_registerName("foo"));
也就是说:
[obj foo];在objc编译时,会被转意为:objc_msgSend(obj, @selector(foo));。
18. 什么时候会报unrecognized selector的异常?
答:
简单来说:
当调用该对象上某个方法,而该对象上没有实现这个方法的时候, 可以通过“消息转发”进行解决。 简单的流程如下,在上一题中也提到过:
objc是动态语言,每个方法在运行时会被动态转为消息发送,即:objc_msgSend(receiver, selector)。
objc在向一个对象发送消息时,runtime库会根据对象的isa指针找到该对象实际所属的类,然后在该类中的方法列表以及其父类方法列表中寻找方法运行,如果,在最顶层的父类中依然找不到相应的方法时,程序在运行时会挂掉并抛出异常unrecognized selector sent to XXX 。

但是在这之前,objc的运行时会给出三次拯救程序崩溃的机会:

// Son.h
@interface Son : Father()
@end
// Son.m
@implementation Son
- (instancetype)init
{
self = [super init];
if (self) {
[self performSelector:@selector(sel) withObject:nil];//当前sel方法未实现
}
return self;
}
1)、动态方法解析
objc运行时会调用+resolveInstanceMethod:或者 +resolveClassMethod:,让你有机会提供一个函数实现。如果你添加了函数并返回YES,那运行时系统就会重新启动一次消息发送的过程,否则 ,运行时就会移到下一步,消息转发(Message Forwarding)。
// Son.m
@implementation Son
//第一次拯救机会
id dynamicMethodIMP(id self, SEL _cmd){
NSLog(@"%s:动态添加的方法",__FUNCTION__);
return @"1";
}
+ (BOOL)resolveInstanceMethod:(SEL)sel {
class_addMethod(self.class, sel, (IMP)(dynamicMethodIMP), "@@:");
[super resolveInstanceMethod:sel];
return YES;//返回NO会继续走消息转发
}
@end
或者:
// Son.m
@implementation Son
+ (BOOL)resolveClassMethod:(SEL)sel {
class_addMethod(object_getClass(self), sel, (IMP)(dynamicMethodIMP), "@@:"); //注意第一个参数的区别
[super resolveClassMethod:sel];
return YES;
}
@end
//调用
[Son performSelector:@selector(sel) withObject:nil];
2)、重定向
如果目标对象实现了-forwardingTargetForSelector:,Runtime 这时就会调用这个方法,给你把这个消息转发给其他对象的机会。 只要这个方法返回的不是nil和self,整个消息发送的过程就会被重启,当然发送的对象会变成你返回的那个对象。否则,就会继续下一步的转发机制。因为这一步不会创建任何新的对象,但下一步转发会创建一个NSInvocation对象,所以相对更快点。
//接盘侠ForwardingTarge.m
@implementation ForwardingTarge
id dynamicMethod(id self, SEL _cmd) {
NSLog(@"%s:动态添加的方法",__FUNCTION__);
return @"1";
}
+ (BOOL)resolveInstanceMethod:(SEL)sel __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0) {
class_addMethod(self.class, sel, (IMP)dynamicMethod, "@@:");
BOOL rslt = [super resolveInstanceMethod:sel];
rslt = YES;
return rslt;
}
- (id)forwardingTargetForSelector:(SEL)aSelector __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0) {
id rslt = [super forwardingTargetForSelector:aSelector];
return rslt;
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
//OBJC_SWIFT_UNAVAILABLE("")
{
id rslt = [super methodSignatureForSelector:aSelector];
return rslt;
}
- (void)forwardInvocation:(NSInvocation *)anInvocation
//OBJC_SWIFT_UNAVAILABLE("")
{
if ([self respondsToSelector:
[anInvocation selector]]) {
[anInvocation invokeWithTarget:self];
} else {
[super forwardInvocation:anInvocation];
}
}
- (void)doesNotRecognizeSelector:(SEL)aSelector {
[super doesNotRecognizeSelector:aSelector]; // crash
}
//Son.m
@interface Son ()
@property (nonatomic, strong) ForwardingTarge *target;
@end
@implementation Son
- (instancetype)init
{
self = [super init];
if (self) {
_target = [ForwardingTarge new];//new一个接盘侠
[self performSelector:@selector(sel) withObject:nil];
}
return self;
}
- (id)forwardingTargetForSelector:(SEL)aSelector __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0) {
id rslt = [super forwardingTargetForSelector:aSelector];
rslt = self.target; //让接盘侠去处理这个方法
return rslt; //返回目标对象不为nil 或者 self,消息转发就不会接续进行
}
3)、 转发
这一步是Runtime最后一次给你挽救的机会。首先它会发送-methodSignatureForSelector:消息获得函数的参数和返回值类型。如果-methodSignatureForSelector:返回nil,Runtime则会发出-doesNotRecognizeSelector:消息,程序这时也就挂掉了。如果返回了一个函数签名,Runtime就会创建一个NSInvocation对象并发送-forwardInvocation:消息给目标对象,两者组合起来使用。
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
{
id rslt = [super methodSignatureForSelector:aSelector];
NSMethodSignature *sig = [NSMethodSignature signatureWithObjCTypes:"v@:"];
rslt = sig;
return rslt;
}
//OBJC_SWIFT_UNAVAILABLE("")
- (void)forwardInvocation:(NSInvocation *)anInvocation
{
// [super forwardInvocation:anInvocation];
[self.target forwardInvocation:anInvocation];//移交给别的类去响应这个方法
}
- (void)doesNotRecognizeSelector:(SEL)aSelector { //不响应,则会走这个方法
// 在crash前 保存crash数据,供分析
[super doesNotRecognizeSelector:aSelector]; // crash
}
Demo:_objc_msgForward_demo ,可运行起来看看。
19. 一个objc对象如何进行内存布局?(考虑有父类的情况)
答:
1)、所有父类的成员变量和自己的成员变量都会存放在该对象所对应的存储空间中.
2)、每一个对象内部都有一个isa指针,指向他的类对象,类对象中存放着本对象的:
- 对象方法列表(对象能够接收的消息列表,保存在它所对应的类对象中)
- 成员变量的列表
- 属性列表
3)、类对象它内部也有一个isa指针指向元对象(meta class),元对象内部存放的是类方法列表,类对象内部还有一个superclass的指针,指向他的父类对象。
每个 Objective-C 对象都有相同的结构,如下图所示:

翻译过来就是:
| - | Objective-C 对象的结构图 |
|---|---|
| - | ISA指针 |
| - | 根类的实例变量 |
| - | 倒数第二层父类的实例变量 |
| - | ... |
| - | 父类的实例变量 |
| - | 类的实例变量 |
4)、根对象就是NSObject,它的superclass指针指向nil
5)、类对象既然称为对象,那它也是一个实例。类对象中也有一个isa指针指向它的元类(meta class),即类对象是元类的实例。元类内部存放的是类方法列表,根元类的isa指针指向自己,superclass指针指向NSObject类。
如图:

20. 一个objc对象的isa的指针指向什么?有什么作用?
答:
指向他的类对象,从而可以找到对象上的方法
- isa:是一个Class 类型的指针,实例对象,类对象,元类对象都有isa指针;
- 对象的isa指向类,类的isa指向元类(meta class),元类isa指向元类的根类。类里面保存了实例方法,元类里面保存了类方法,可以使用isa帮助一个对象找到它想调用的方法(前提是实现了该方法);
- 实例对象调用实例方法时会先从本类的实例方法列表中开始寻找,当找不到会往上,在父类的实例方法列表中寻找;
- 当类方法被调用时,先会从本身查找类方法的实现,如果没有,元类会向他父类查找该方法;
- 元类(meteClass)也是类,它也是对象。元类也有isa指针,它的isa指针最终指向的是一个根元类(root meteClass)。根元类的isa指针指向本身,根元类的父类是NSObject,NSObject的isa指向根元类,父类指向nil,这样形成了一个封闭的内循环。