以下题目来自 招聘一个靠谱的iOS ,笔者整理了大部分,并将个人的理解和想法写出来,可能存在于原作者的参考答案不同的地方,还请大家各抒己见!
copy关键字只能应用于对象,不能用于基本类型。copy属性会复制一份,并且强引用之,但是对于集合类型,通常并不能达到深拷贝的目的。NSString、NSArray、NSDictionary等经常使用copy关键字,是因为他们有对应的可变类型:NSMutableString、NSMutableArray、NSMutableDictionary,当然很多时候都使用了strong来声明。block也使用copy关键字来声明。
例如:
// 使用copy声明,生成的实际上是NSArray类型 @property (copy) NSMutableArray *mutableArray; NSMutableArray *array = [NSMutableArrayarrayWithObjects:@1,@2,nil]; self.mutableArray = array; // 调用删除操作,会崩溃,因为所生成的对象类型实际上是NSArray类型,是不可变的 [self.mutableArrayremoveObjectAtIndex:0]; // Crash信息 -[__NSArrayI removeObjectAtIndex:]: unrecognized selectorsenttoinstance 0x7fcd1bc30460
@property = ivar(实例变量) + getter(取方法) + setter(存方法)
“属性” (property)有两大概念:ivar(实例变量)、存取方法(access method = getter + setter)
这是编译器自动合成的,通过@synthesize关键字指定,若不指定,默认为@synthesize propertyName = _propertyName;若手动实现了getter/setter方法,则不会自动合成。
现在编译器已经默认为我们添加@synthesize propertyName = _propertyName;因此不再需要手动添加了,除非你真的要改成员变量名。
生成getter方法时,会判断当前属性名是否有_,比如声明属性为@property (nonatomic, copy) NSString *_name;那么所生成的成员变量名就会变成__name,如果我们要手动生成getter方法,就要判断是否以_开头了。
不过,命名都要有规范,是不允许声明属性是使用_开头的,不规范的命名,在使用runtime时,会带来很多的不方便的。
如果想了解更多关于runtime方面的知识,请阅读runtime专题
objc_setAssociatedObject objc_getAssociatedObject
如果想了解更多关于runtime方面的知识,请阅读runtime关联属性
// 声明一个weak属性,这里假设delegate,其实weak关键字可以不使用, // 因为我们重写了getter/setter方法 @property (nonatomic, weak) id delegate; - (id)delegate { return objc_getAssociatedObject(self, @"__delegate__key"); } // 指定使用OBJC_ASSOCIATION_ASSIGN,官方注释是: // Specifies a weak reference to the associated object. // 也就是说对于对象类型,就是weak了 - (void)setDelegate:(id)delegate { objc_setAssociatedObject(self, @"__delegate__key", delegate, OBJC_ASSOCIATION_ASSIGN); }
@property (nonatomic, getter=isOn) BOOL on;
对于weak声明的属性,都不需要在dealloc中指定为nil,在ARC下,编译器会自动帮助我们处理。即使编译器不帮助我们处理,我们也不需要手动在dealloc中设置为nil。
@property有两个对应的词,一个是@synthesize,,另一个是@dynamic。如果 @synthesize和@dynamic都没写,那么默认的就是@syntheszie var = _var;这两个关键字都是为@property关键字工作的。
参考链接:
在Objective-C中向nil发送消息是完全有效的,只是在运行时不会有任何作用,因为在运行时调用时,objc_msgSend函数传过去的receiver是nil,而内部会判断receiver是否为nil,若为nil则什么也不干。同样,若cmd也就是selector为nil,也是什么也不干。
实际上,编译器在编译时会转换成objc_msgSend,大概会像这样:
((void (*)(id, SEL))(void)objc_msgSend)((id)obj, sel_registerName("foo"));
也就是说,[obj foo];在objc动态编译时,会被转换为:objc_msgSend(obj, @selector(foo));这样的形式,但是需要根据具体的参数类型及返回值类型进行相应的类型转换。
下面只讲述对象方法的解析过程:
更新详细地,请阅读 runtime message forwarding
先阅读下图:
从图中可以清晰地看出,一个对象的isa指针指向的是他所属的类(class),用于查找对象上的方法。而类对象的isa指针又指向它的元类(meta class),无类的isa也指向根类的元类,而根类的元类的isa又指向根类本身。
@implementationSon: Father - (id)init { self = [super init]; if (self) { NSLog(@"%@", NSStringFromClass([self class])); NSLog(@"%@", NSStringFromClass([super class])); } return self; } @end // 输出 NSStringFromClass([self class]) = Son NSStringFromClass([super class]) = Son
这个题目主要是考察关于Objective-C中对self和super的理解。我们都知道:self是类的隐藏参数,指向当前调用方法的这个类的实例。那super呢?
很多人会想当然的认为“super和self类似,应该是指向父类的指针吧!”。这是很普遍的一个误区。其实 super是一个 Magic Keyword,它本质是一个编译器标示符,和self 是指向的同一个消息接受者!他们两个的不同点在于:super会告诉编译器,调用class 这个方法时,要去父类的方法,而不是本类里的。
上面的例子不管调用[self class]还是[super class],接受消息的对象都是当前 Son *xxx 这个对象。
当使用self调用方法时,会从当前类的方法列表中开始找,如果没有,就从父类中再找;而当使用super时,则从父类的方法列表中开始找。然后调用父类的这个方法。
通过self来调用方法时,会转换成:
id objc_msgSend(id self, SEL op, ...)
而通过super调用方法时,会转换成:
id objc_msgSendSuper(struct objc_super*super, SEL op, ...)
而第一个参数是 objc_super 这样一个结构体,其定义如下:
struct objc_super { __unsafe_unretainedid receiver; __unsafe_unretainedClass super_class; };
如下图所示,每个selector都与对应的IMP是一一对应的关系,通过selector就可以直接找到对应的IMP:
_objc_msgForward是IMP类型,用于消息转发的:当向一个对象发送一条消息,但它并没有实现的时候,_objc_msgForward会尝试做消息转发。
IMP msgForward = _objc_msgForward;
如果手动调用 objc msgForward,将跳过查找IMP的过程,而是直接触发“消息转发”,进入如下流程:
更新详细地,请阅读 runtime message forwarding
runtime对注册的类会进行布局,对于weak对象会放入一个hash表中。 用weak指向的对象内存地址作为key,当此对象的引用计数为0的时候会dealloc。假如weak指向的对象内存地址是a,那么就会以a为键,在这个 weak 表中搜索,找到所有以a为键的weak对象,从而设置为nil。
weak修饰的指针默认值是nil(在Objective-C中向nil发送消息是安全的)
因为编译后的类已经注册在runtime中,类结构体中的objc_ivar_list实例变量的链表 和instance_size实例变量的内存大小已经确定,同时runtime会调用class_setIvarLayout或 class_setWeakIvarLayout来处理strong和weak引用。所以不能向存在的类中添加实例变量;
运行时创建的类是可以添加实例变量,调用class_addIvar函数。但是得在调用 objc_allocateClassPair之后,objc_registerClassPair之前,原因同上。
关注 | 账号 | 备注 |
---|---|---|
Swift/ObjC技术群一 | 324400294 | 群一若已满,请申请群二 |
Swift/ObjC技术群二 | 494669518 | 群二若已满,请申请群三 |
Swift/ObjC技术群三 | 461252383 | 群三若已满,会有提示信息 |
关注微信公众号 | iOSDevShares | 关注微信公众号,会定期地推送好文章 |
关注新浪微博账号 | 标哥Jacky | 关注微博,每次发布文章都会分享到新浪微博,即可时时阅读文章 |
关注标哥的GitHub | CoderJackyHuang | 这里有很多的Demo和开源组件,大家可以关注哦! |
关于我 | 进一步了解标哥 | 大家若对笔者感兴趣,可以关注我哦!如果觉得文章对您很有帮助,可捐助我! |