【面试题】Objective-C 相关
@property 、 @synthesize 与 @dynamic 的作用
- @property: 用于声明成员变量的 getter/setter 方法
- @synthesize: 与 @property 配套使用,@synthesize 会自动生成一个
_
开头的成员变量(若是不指定的话),并实现 @property 声明的 getter/setter 方法。 - @dynamic: 不会自动生成成员变量,程序员需自己添加成员变量并实现 getter/setter 方法。
具体细节详见以下代码:
#import <Foundation/Foundation.h>
@interface JQPerson : NSObject
{
// 3.1 由于 @dynamic 并不会自动生成成员变量,因此需自主添加成员变量用于 getter/setter 方法,否则会报 “Use undeclared identifier” 错误。
NSInteger _weight;
}
// 1. @property 的简单使用
@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) NSInteger age;
@property (nonatomic, assign) NSInteger height;
@property (nonatomic, assign) NSInteger weight;
@end
@implementation JQPerson
// 2. @synthesize 的使用
// 2.1 默认生成"_"开头的成员变量, 即:
@synthesize name = _name;
// 2.2 生成与 @property 相同的不带下划线的成员变量:
@synthesize age;
// 2.3 指定生成的成员变量名:
@synthesize height = H;
// 3. @dynamic 的使用
@dynamic weight;
// 3.2 实现 getter/setter 方法(这里只是简单的实现):
- (void)setWeight:(NSInteger)weight {
_weight = weight;
}
- (NSInteger)weight {
return _weight;
}
@end
(Tips:在都没有使用 @synthesize 以及 @dynamic 时,默认为 @synthesize propertyName = _propertyName;
。但若是同时实现了 getter&setter 方法,则隐含表示为 @dynamic propertyName;
因此编译器并不会自动生成成员变量,此时若是使用成员变量则会出现 “Use undeclared identifier” 错误。解决方法可以在类的声明中自主添加私有的成员变量,或者使用 @synthesize,告知编译器自动生成成员变量。)
@property 有哪些关键字?
@property
的关键字主要分为以下几类:
- 原子性(
nonatomic/atomic
):- atomic: atomic 是线程安全的,但是执行效率不高。使用 atomic 关键字,编译器会自动为属性的 getter&setter 方法中加入一些互斥加锁代码,以保证在多个线程的情况下,不会出现一条线程调用 setter 方法但未执行完之前,另一条线程也调用 setter 方法,保证数据的有效性。也正是因为加锁的原因,会消耗一定的性能,影响效率。
- nonatomic: nonatomic 不是线程安全的,如果对象无需考虑多线程的情况,可以使用 nonatomic 关键字,编译器便不会生成多余的加锁代码,可以提高执行效率。开发中一般常用 nonatomic 关键字。
- 读写权限(
readonly/readwrite
):- readonly: 只读关键字,只可调用对象的 getter 方法。
- readwrite: 可读可写关键字,对象的 getter&setter 方法均可调用。
- 内存管理语义(
assign/strong/weak/unsafe_unretained/copy/retain
):- assign: 常用于基本数据类型(CGFloat、NSInteger等)。用于直接赋值,不必考虑内存管理,因为基本数据类型一般是分配在栈上的,而栈的内存会由系统自动处理。
- strong: 强引用,引用存在对象即存在。
- weak: 弱引用,只要对象没有强引用指向便会被释放,且 dealloc 会将指针置为 nil。
- unsafe_unretained: 与 assign 及 weak 相似,但 unsafe_unretained 一般用于对象,且被释放后指针并不会被置为 nil,比较少用。
- copy: 与 retain 相似,成员变量的 setter 方法会先 release 旧值, 再 copy 新值,retainCount 为 1。一般用于 NSString、block 等。内容拷贝。
- retain: 在非ARC环境中,成员变量的 setter 方法会先 release 旧值,再 retain 新值,retainCount + 1。指针拷贝。
getter&setter
方法名:
自定义属性的 getter&setter 方法名,一般常用于BOOL
类型属性的 getter 方法,如:@property (getter=isOpen)BOOL *open;
。- **可否为空
Nullability Annotation
**(nullable/nonnull/null_resettable/null_unspecified
):为了与 swift 混编,适配 swift 中的 optional 可选类型而新增的关键字,比较少用到。
以下代码的打印结果是什么?为什么?
@interface JQApple : JQFruit
@end
@implementation JQApple
- (instancetype)init{
self = [super init];
if (self) {
NSLog(@"%@", NSStringFromClass([self class]));
NSLog(@"%@", NSStringFromClass([super class]));
}
return self;
}
@end
按照面向对象的思想应该是分别打印JQApple
和JQFruit
然而运行结果却出乎我们的意料,最终均都打印“JQApple”。这是为什么?
因为self
是类的隐藏参数,指向当前调用方法的对象。而super
并不是如我们所想是指向当前对象父类的指针。其实super
是一个编译器标识符,在运行时中与self
相同,指向同一个消息接受者,只是self
会优先在当前类的methodLists
中查找方法,而super
则是优先从父类中查找。验证如下:
在终端运行:
$ clang -rewrite-objc main.m
可以看到运行时代码如下:
NSLog((NSString *)&__NSConstantStringImpl__var_folders_ld_03322m393b5cyvhz2zhv2c100000gn_T_main_97554f_mi_0, NSStringFromClass(((Class (*)(id, SEL))(void *)objc_msgSend)((id)self, sel_registerName("class"))));
NSLog((NSString *)&__NSConstantStringImpl__var_folders_ld_03322m393b5cyvhz2zhv2c100000gn_T_main_97554f_mi_1, NSStringFromClass(((Class (*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("JQApple"))}, sel_registerName("class"))));
删除相关无关类型及方法后代码如下:
objc_msgSend((id)self, sel_registerName("class"));
objc_msgSendSuper((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("JQApple"))}, sel_registerName("class"));
查看函数定义:
/**
* Sends a message with a simple return value to an instance of a class.
*
* @param self A pointer to the instance of the class that is to receive the message.
* @param op The selector of the method that handles the message.
* @param ...
* A variable argument list containing the arguments to the method.
*
* @return The return value of the method.
*
* @note When it encounters a method call, the compiler generates a call to one of the
* functions \c objc_msgSend, \c objc_msgSend_stret, \c objc_msgSendSuper, or \c objc_msgSendSuper_stret.
* Messages sent to an object’s superclass (using the \c super keyword) are sent using \c objc_msgSendSuper;
* other messages are sent using \c objc_msgSend. Methods that have data structures as return values
* are sent using \c objc_msgSendSuper_stret and \c objc_msgSend_stret.
*/
OBJC_EXPORT id objc_msgSend(id self, SEL op, ...)
__OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);
/**
* Sends a message with a simple return value to the superclass of an instance of a class.
*
* @param super A pointer to an \c objc_super data structure. Pass values identifying the
* context the message was sent to, including the instance of the class that is to receive the
* message and the superclass at which to start searching for the method implementation.
* @param op A pointer of type SEL. Pass the selector of the method that will handle the message.
* @param ...
* A variable argument list containing the arguments to the method.
*
* @return The return value of the method identified by \e op.
*
* @see objc_msgSend
*/
OBJC_EXPORT id objc_msgSendSuper(struct objc_super *super, SEL op, ...)
__OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);
可知objc_msgSend
函数中的self
参数是指指向接收消息的类的实例的指针,即消息接受者,而op
参数则是指处理该消息的selector
;objc_msgSendSuper
函数中的参数super
则是一个objc_super
结构体,objc_super
结构体定义如下:
/// Specifies the superclass of an instance.
struct objc_super {
/// Specifies an instance of a class.
__unsafe_unretained id receiver;
/// Specifies the particular superclass of the instance to message.
__unsafe_unretained Class super_class;
/* super_class is the first class to search */
};
其中receiver
是指类的实例,super_class
则是指该实例的父类。可以看到在编译后的C++
代码中有个__rw_objc_super
结构体:
struct __rw_objc_super {
struct objc_object *object;
struct objc_object *superClass;
__rw_objc_super(struct objc_object *o, struct objc_object *s) : object(o), superClass(s) {}
};
其实即为objc_super
结构体。通过(__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("JQApple"))}
该段代码可知:我们把self
以及JQApple
的父类通过结构体的构造方法构造了一个__rw_objc_super
结构体,也就是objc_super
。因此objc_super
结构体中的receiver
既是self
。所以[self class]
和[super class]
指向的是同一个消息接受者,只是self
会优先从当前类的实现中寻找方法处理消息,而super
则是会优先从objc_super
结构体中的super_class
也就是父类的实现中查找。JQFruit
及JQApple
中均未实现- (Class)class;
方法,因此会逐级向上查找最终调用基类NSObject
的- (Class)class;
方法,通过官方开源的NSObject
的- (Class)class;
方法代码:
- (Class)class{
return object_getClass(self);
}
可知,消息接受者是self
,而[self class]
和[super class]
指向的是同一个消息接受者,因此该段代码均打印JQApple
。