Objective-C is a rapidly evolving language, in a way that you just don't see in established programming languages. ARC, object literals, subscripting, blocks: in the span of just three years, so much of how we program in Objective-C has been changed (for the better).
All of this innovation is a result of Apple's philosophy of vertical integration. Just as Apple's investment in designing its own chipsets gave them leverage to compete aggressively with their mobile hardware, so too has their investment in LLVM allowed their software to keep pace.
Clang developments range from the mundane to paradigm-changing, but telling the difference takes practice. Because we're talking about low-level language features, it's difficult to understand what implications they may have higher up with API design.
One such example is instancetype
, the subject of this week's article.
In Objective-C, conventions aren't just a matter of coding best-practices, they are implicit instructions to the compiler.
For example, alloc
and init
both have return types of id
, yet in Xcode, the compiler makes all of the correct type checks. How is this possible?
In Cocoa, there is a convention that methods with names like alloc
, or init
always return objects that are an instance of the receiver class. These methods are said to have a related result type.
Class constructor methods, although they similarly return id
, don't get the same type-checking benefit, because they don't follow that naming convention.
You can try this out for yourself:
[[[NSArray alloc] init] mediaPlaybackAllowsAirPlay]; // ❗ "No visible @interface for `NSArray` declares the selector `mediaPlaybackAllowsAirPlay`"
//alloc and init 将会根据命名约定返回“相关结果类型”(此处是NSArray)
[[NSArray array] mediaPlaybackAllowsAirPlay]; // (No error before ios7,while it will be error after ios7,because
// the method array return instancetype after ios7 and the error is the same to above
)
//array 不遵守命名约定,返回id类型,所以如果返回值为id类型,那么在此处就不会有错误,如果返回值为instancetype就会出错,ios7对这点bug做了改进的
//after ios7
+ (instancetype)array
//before ios7
+(id)array
//so you see the difference!
Because alloc
and init
follow the naming convention for being a related result type, the correct type check against NSArray
is performed. However, the equivalent class constructor array
does not follow that convention, and is interpreted as id
.
id
is useful for opting-out of type safety, but losing it when you do want it sucks.
The alternative, of explicitly declaring the return type ((NSArray *)
in the previous example) is a slight improvement, but is annoying to write, and doesn't play nicely with subclasses.
This is where the compiler steps in to resolve this timeless edge case to the Objective-C type system:
instancetype
is a contextual keyword that can be used as a result type to signal that a method returns a related result type. For example:
@interface Person
+ (instancetype)personWithName:(NSString *)name;
@end
instancetype
, unlikeid
, can only be used as the result type in a method declaration.
With instancetype
, the compiler will correctly infer that the result of +personWithName:
is an instance of a Person
.
Look for class constructors in Foundation to start using instancetype
in the near future. New APIs, such as UICollectionViewLayoutAttributes are using instancetype
already.
Further Implications
Language features are particularly interesting because, again, it's often unclear of what impact they'll have on higher-level aspects of software design.
While instancetype
may seem to be a rather mundane, albeit welcome addition to the compiler, it can be used to some rather clever ends.
Jonathan Sterling wrote this quite interesting article, detailing how instancetype
could be used to encode statically-typed collections, without generics:
NSURL <MapCollection> *sites = (id)[NSURL mapCollection];
[sites put:[NSURL URLWithString:@"http://www.jonmsterling.com/"]
at:@"jon"];
[sites put:[NSURL URLWithString:@"http://www.nshipster.com/"]
at:@"nshipster"];
NSURL *jonsSite = [sites at:@"jon"]; // => http://www.jonmsterling.com/
Statically-typed collections would make APIs more expressive--no longer would a developer be unsure about what kinds of objects are allowed in a collection parameter.
相关推荐
- (instancetype)init; - (void)push:(id)object; - (id)pop; - (id)peek; - (BOOL)isEmpty; @end @implementation Stack - (instancetype)init { self = [super init]; if (self) { _array = [NSMutableArray...
instancetype和id的区别在于,instancetype可以在编译的时候判断对象的真实类型,而id在编译的时候不能判断对象的真实类型。instancetype只能用于作为返回值,它会进行类型检查,如果创建出来的对象,赋值了不相干的...
1. **声明一个类方法**:`+ (instancetype)sharedInstance;` 这个方法返回单例对象,是全局获取单例的主要途径。 2. **私有化初始化方法**:`- (instancetype)init NS_UNAVAILABLE;` 防止其他代码直接通过`init`...
- `+ (instancetype)arrayWithObjects:(const id [])objects count:(NSUInteger)cnt`: 使用给定的对象列表创建一个数组。 - **实例方法**: - `-(NSUInteger)count`: 返回数组中的元素个数。 - `-(id)...
- (instancetype)initWithFrame:(CGRect)frame { if (self = [super initWithFrame:frame]) { LXFWeakTarget *weakTarget = [[LXFWeakTarget alloc] initWithTarget:self]; [weakTarget addTimerWithTimeInterval...
- `+ (instancetype)arrayWithObject:(id)object`: 创建并返回包含单个对象的数组。 **实例方法** - `-(NSUInteger)count`: 返回数组中的元素数量。 - `- (id)objectAtIndex:(NSUInteger)index`: 返回指定索引位置...
1. 初始化方法:使用`+ (instancetype)setWithObject:`或`+ (instancetype)setWithObjects:(const id _Nonnull *)objects count:(NSUInteger)cnt`来创建包含单个或多个元素的集合。 2. 使用数组初始化:通过`+ ...
'ImageId': 'ami-0c94855ba95c71c99', # 替换为你的AMI ID 'InstanceType': cheapest_instance_type, 'SecurityGroupIds': ['sg-0123456789abcdef0'], # 替换为你的安全组ID 'KeyName': 'your_key_pair' # 替换...
request.InstanceType = '<instance_type>' # 实例类型 request.Zone = '<zone>' # 可用区 request.SecurityGroupIds = ['<security_group_id>'] # 安全组ID request.VpcId = '<vpc_id>' # 私有网络ID request....
在JavaScript中,`instanceof`和`typeof`是两种用于检查变量类型的运算符,它们各自具有独特的用途和特点。理解这两个运算符的区别是理解和编写高效、健壮的JavaScript代码的关键。 首先,`instanceof`运算符主要...
其他参数如`ImageId`、`InstanceType`和`SecurityGroupId`也需要根据实际情况填写。 总的来说,`ecs_composex`库为Python开发者提供了便捷的接口来操作阿里云ECS服务,简化了云计算资源的管理和维护,极大地提高了...
- ( instancetype )interpolateToValue:( id )toValue progress:( double )progress behavior:(LNInterpolationBehavior)behavior; Swift public func interpolate(to toValue: Any, progress: Double) -> Self ...
- **`+ (instancetype)new`**:类方法,用于创建并返回一个新的实例。此方法通常会调用 `-init` 方法来初始化新创建的对象。 - **`- (id)copy`**:创建并返回对象的副本。通常用于实现不可变数据类型的复制。 - **`-...
+ (instancetype)sharedInstance { static DatabaseManager *sharedInstance = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ sharedInstance = [[self alloc] init]; // 创建数据库...
InstanceType: 'ecs.t5-lc1m1.small' SecurityGroupId: 'sg-xxx' ``` 这个模板会创建一个使用CentOS镜像的小型ECS实例,并指定安全组。 5. **最佳实践** - 使用版本控制工具(如Git)管理ROS模板,确保代码可...
@protocol ZSegmentedControlDelegate ...@property (nonatomic, weak)id <ZSegmentedControlDelegate> delegate; //@property (nonatomic, copy) void (^indexChangeBlock)(NSUInteger index);
+ (instancetype)sharedInstance { static dispatch_once_t onceToken; static id sharedInstance; dispatch_once(&onceToken, ^{ sharedInstance = [[self alloc] init]; }); return sharedInstance; } ``` ...
- (id)init { self = [super init]; if (self) { // 初始化操作 } return self; } // 防止其他方式实例化单例 - (instancetype)init NS_UNAVAILABLE; + (instancetype)new NS_UNAVAILABLE; @end ``` 这里使用...
req.InstanceType = "your_instance_type" req.Zone = "your_zone" # 获取响应 resp = client.CreateInstances(req) print(resp.Response) ``` 以上代码展示了如何使用`tencentcloud-sdk-python-ba`创建一个云...