Objective-C关键字__Nullable和__Nonnull

本文介绍了Xcode 6.3引入的_nullable_和_nonnull_关键字,这些关键字增强了Objective-C的安全性,类似于Swift的Optional。文章详细阐述了它们的用途、兼容性和实际应用,帮助开发者更好地理解和使用这些关键字来提高代码的清晰度和安全性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Xcode 6.3中有一项新特性,`nullability`标记,这为 OC 提供了类似 Swift 中的 Optional 的类型 为了让更多开发者从 OC 向 Swift 过渡,OC 和 Swift 混编已经变得尤为重要,就像是OC通向Swift的一座桥梁, 通过引入泛型使得 OC 和 Swift 之间更加安全清楚的理解和共享包含特定元素的集合

The Core: _Nullable and _Nonnull

关于 _Nullable 和 _Nonnull ,官方文档有这么一句话
At the core of this feature we have two new type annotations: _Nullable and _Nonnull.
As you might expect, a _Nullable pointer may have a NULL or nil value, while a _Nonnull one should not.
The compiler will tell you if you try to break the rules.

大概意思就是,我们又添加了两种类型,_Nullable可以为NULL 或者nil,反之 _Nonnull绝对不能为空,否则编译器就会出现警告

@interface AAPLList : NSObject <NSCoding, NSCopying>
// ...
- (AAPLListItem * _Nullable)itemWithName:(NSString * _Nonnull)name;
@property (copy, readonly) NSArray * _Nonnull allItems;
// ...
@end
// --------------
[self.list itemWithName:nil]; // warning!

事实上你可以在任何可以使用C语言关键字const的地方使用_Nullable_Nonnull
当然 _Nullable_Nonnull 一般用于指针类型,在一般情况下,有一个更好的方法去实现这种标记的功能
。即在方法申明的时候可以使用non-underscored形式,nullablenonnull,直接写在类型前面(类型必须是一个对象或者block指针)。

- (nullable AAPLListItem *)itemWithName:(nonnull NSString *)name;
- (NSInteger)indexOfItem:(nonnull AAPLListItem *)item;

在属性中,同样可以使用non-underscored形式将这样的关键字放到属性列表中

@property (copy, nullable) NSString *name;
@property (copy, readonly, nonnull) NSArray *allItems;

non-underscored方式比underscored方式有很明显的优势,但是即便如此,你仍然需要将它们写在每一种类型中去
为了更加高效的工作和代码的简洁,我们需要用到一种苹果称之为audited regions的方法

什么是 Audited Regions

为了方便采用新的方式,你可以在Objective-C头文件标记一块区域作为audited for nullability,在这块区域内,
任何简单指针类型都将被标注为nonnull类型,这样一来,如果有一大段代码需要加注nonnull,就比前面那种繁琐的方式简单多了。

NS_ASSUME_NONNULL_BEGIN
@interface AAPLList : NSObject <NSCoding, NSCopying>
// ...
- (nullable AAPLListItem *)itemWithName:(NSString *)name;
- (NSInteger)indexOfItem:(AAPLListItem *)item;

@property (copy, nullable) NSString *name;
@property (copy, readonly) NSArray *allItems;
// ...
@end
NS_ASSUME_NONNULL_END

// --------------

self.list.name = nil;   // okay

AAPLListItem *matchingItem = [self.list itemWithName:nil];  // warning!

安全起见,这种方式会有一些异常

1.typedef类型不会被当做nonnull处理,即使在audited regions
2.很多复杂的指针,譬如id *必须明确标注,例如要制定一个非空的指针可以被空对象引用,要这样写_Nullable id * _Nonnull
3.特定类型 NSError ** 经常通过方法的参数返回错误以致它总是被假定为一个可以被空指针引用的空指针

兼容性

如果已经存在的Objective-C代码被重写会怎样?单单以这种方式改变类型安全么?没错,是安全的。

现有的编译代码会在你的框架上继续跑,即ABI不会变,就是说现有的代码在运行时出现nil不会报错
当你使用新的Swift编译器,使用不安全的方式在编译时现有的源代码在你的框架可能会有额外的警告
nonnull不影响优化,尤其是在运行时你仍然能检查标注为nonnull的参数是否是nil,向后兼容是很重要的。

这个功能最早被发布在Xcode 6.3上,关键字是__nullable__nonnull,由于一些三方库潜在的冲突,
Xcode 7上将关键字改为这里提到的_Nullable_Nonnull,为了兼容Xcode 6.3,苹果定义了
__nullable__nonnull

回到Swift

现在我们已经nullability标注添加到我们的Objective-C头文件中,来看看怎么在Swift中使用它
在没有添加标注前的Objective-C转换成Swift应该是这样的

class AAPLList : NSObject, NSCoding, NSCopying { 
  // ...
  func itemWithName(name: String!) -> AAPLListItem!
  func indexOfItem(item: AAPLListItem!) -> Int

  @NSCopying var name: String! { get set }
  @NSCopying var allItems: [AnyObject]! { get }
  // ...
}

添加之后是这样的

class AAPLList : NSObject, NSCoding, NSCopying { 
  // ...
  func itemWithName(name: String) -> AAPLListItem?
  func indexOfItem(item: AAPLListItem) -> Int

  @NSCopying var name: String? { get set }
  @NSCopying var allItems: [AnyObject] { get }
  // ...
}

这样一来,Swift代码看起来就简洁多了,虽然是一个微妙的变化,但这会让你的框架更完美

### 数据处理与分析 为了实现这些目标,可以按照以下方法分别解决三个研究问题。 #### Q1: 找出一个月中事故最多的日期、周几天气状况 可以通过 Spark SQL DataFrame API 来完成此任务。首先加载数据并解析时间字段以及天气条件字段。接着按天聚合计数,并提取星期几的信息。最后筛选最大值即可得到结果。 ```sql -- 假设表名为 accident_data, 时间列名 incident_time, 天气状态列为 weather_condition SELECT date_format(incident_time, 'yyyy-MM-dd') AS day, date_format(incident_time, 'E') AS weekday, weather_condition, COUNT(*) as count_accidents FROM accident_data WHERE month(incident_time) = desired_month AND year(incident_time) = desired_year -- 替换为具体月份年份 GROUP BY 1, 2, 3 ORDER BY count_accidents DESC LIMIT 1; ``` 这段代码会返回指定月内发生最多事故的具体一天及其对应的星期名称还有当时的气象情况[^1]。 #### Q2: 构建统计模来探索道路运行速度与温度之间的关系 对于这种类的回归问题,可以选择线性回归作为初步尝试的方法之一。先准备训练集,其中包含平均车速(speed_avg)环境气温(temp_celsius),再调用 MLlib 中的相关类创建模实例进行拟合操作。 ```scala import org.apache.spark.ml.regression.LinearRegression val lrModel = new LinearRegression() .setMaxIter(10) .setRegParam(0.3) .setElasticNetParam(0.8) // Assuming `data` is a Dataset with columns ["speed", "temperature"] val Array(trainingData, testData) = data.randomSplit(Array(0.7, 0.3)) lrModel.fit(trainingData).transform(testData).show(false) ``` 这里展示了如何设置超参数并通过交叉验证优化它们的过程[^2]。 #### Q3: 创建基于机器学习的80号公路交通事故分类器 针对这一需求,可采用随机森林或者梯度提升树这样的集成算法来进行预测工作。同样需要准备好特征向量(比如时间段、车辆数量等),标签则代表不同种类别的事件类。 ```scala import org.apache.spark.ml.classification.RandomForestClassifier val rf = new RandomForestClassifier() .setLabelCol("label") .setFeaturesCol("features") .setNumTrees(20) rf.fit(trainDataset).save("/path/to/model") // Save the trained model to disk. ``` 以上片段说明了怎样定义一个随机森林分类器对象,并将其应用于给定的数据集中去训练最终保存下来的模文件路径处[^3]。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值