iOS的NSObject *, id和instancetype

用id修饰和NSObject *修饰有何不同?

OC中的动态类型和静态类型

  • 动态类型

动态类型指的是对象指针类型的动态性,具体指使用id修饰后将对象的类型确定推迟到运行时,
由赋给它的对象类型决定对象指针的类型,也就是说id修饰的对象为动态类型对象,
其他在编译器指明类型的为静态类型对象,通常如果不需要涉及到多态的话,还是尽量使用静态类型(原因:
错误可以在编译器提前查出,可读性好).

// 动态类型
id obj = [[TestObject alloc] init]
  • 静态类型

一个指针变量指向特定类的对象时,使用的是静态类型,在编译的时候就知道这个
指针变量所属的类, 使用静态类型时, 编译器在编译期间,会做许多的类型检测
因为编译器需要知道哪个对象该如何使用.

// 静态类型
TestObject obj = [[TestObject alloc] init];

苹果官方objc.h文件里有关于Id的定义

// A pointer to an instance of a class
typedef struct objc_object *id;

id可以 用于指向所有的Objective-C对象,是一种万能指针,类似于C语言中的void *
与id类似 NSObject *可以指向所有继承自NSObject的对象,因为在Objective-C中
NSObject是基类, 绝大多数的类继承自NSObject, 因此根据面向对象编程的多态特性
NSObject (可以指向Objective-C中绝大多数的类实例对象

下面测试TestObject类的源码:

// TestObject.h
#import 

@interface TestObject : NSObject

- (void)run;

@end
// TestObject.m
#import "TestObject.h"

@implementation TestObject

- (void)run {
    NSLog(@"%s",__func__);
}

@end

接下来我们在ViewController中运行以下测试id修饰时的代码:

 - (void)viewDidLoad {
    [super viewDidLoad];
    id p1 = [[TestObject alloc] init];
    [p1 run];
}

运行情况是,编译通过,并且p1成功调用了run方法
如果将id改为NSObject *修饰的代码如下:
在这里插入图片描述
编译无法通过,报错:NSObject中没有声明run方法。

  • 使用id修饰的对象是动态类型,编译器在编译期不会去判断其真实类型,因此id指向的对象不管向其发送任何消息,编译器在编译期都不会有任何报错

  • 使用NSObject *修饰的对象是静态类型,在编译期就已经明确该对象是NSObject对象,因此当我们对该对象发送NSObject没有声明的方法时,编译器就会果断报错

id和instancetype的区别与联系

id和instancetype的区别要从关联返回类型和非关联返回类型说起

  • 关联返回类型
    1 类方法中,以alloc或new开头
    2 实例方法中,以autorelease, init,retain或self开头
    当方法会话值为id类型时,编译器不会返回一个类型不明的对象,会返回一个方法所在类类型的对象
    这些方法就被称为是关联对象返回类型的方法,换句话说,这些方法的返回结果以方法所在的
    类为类型
  • 非关联返回类型
    与关联返回类型相反
    1 类方法中,不以alloc或new开头
    2 实例方法中,不以autorelease, init, retain或self开头
    当方法返回值为id类型时, 编译器会返回一个类型不明的对象,即id类型的对象
// TestObject.h
#import 

@interface TestObject : NSObject

+ (id)newTestObject;

+ (id)allocTestObject;

+ (id)fuckTestObject;

- (void)smallMethod;

@property (nonatomic,copy) NSString *name;

@end
// TestObject.m
#import "TestObject.h"

@implementation TestObject

+ (id)newTestObject {
    return [[TestObject alloc] init];
}

+ (id)allocTestObject {
    return [[TestObject alloc] init];
}

+ (id)fuckTestObject {
    return [[TestObject alloc] init];
}

- (void)smallMethod {
    NSLog(@"%s",__func__);
}

@end

iOS的NSObject *, id和instancetype_第1张图片
如图所示,所有返回值调用方法都可以通过编译, 因为返回的都是id类型,但是使用点语法时,fuckTestObject
的返回值报错, 因为此方法的返回值不是关联返回类型,返回的不是TestObject类型,而是id类型,
instancetype其实就是为了扩大关联返回类型的范围,让不是以上述关键字开头的方法的返回值也是关联返回类型

+ (instancetype)fuckTestObject {
    return [[TestObject alloc] init];
}

当我们把fuckTestObject方法的返回值类型从id换成instancetype后,编译就通过了,也就是说返回值是关联返回类型了。

总结一下区别与联系:

id和instancetype都可以做方法的返回值。

id类型的返回值在编译期不能判断对象的真实类型,即非关联返回类型,instancetype类型的返回值在编译期可以判断对象的真实类型,即关联返回类型。

id可以用来定义变量, 可以作为返回值, 可以作为形参,instancetype只能用于作为返回值。

你可能感兴趣的:(面试[)