Swift中FileHandle子类带参自定义方法调用报错,扩展中运行正常
问题原因分析与解决方案
核心报错原因
- 直接诱因是类型不匹配:你调用
writeSamples时所用的实例类型是FileHandle(父类),而非你自定义的CSVFile子类,父类本身没有定义writeSamples方法,自然会报成员不存在的错误。
你之前两个无参方法可正常调用,是因为你直接使用CSVFile的实例调用;而调用writeSamples时,你大概率是拿openFile()方法的返回值进行调用——该方法的返回类型明确是FileHandle,返回的是苹果原生的父类实例,和你写的子类没有任何关系。 - 底层设计问题:
FileHandle属于苹果的类簇类型,官方不推荐直接子类化。类簇的构造方法返回的实际类型往往是内部私有的子类,而非你自定义的子类,强制继承很容易出现类型不匹配、方法找不到的问题。你把writeSamples移到FileHandle扩展后报错消失,是因为扩展会给所有FileHandle类型的实例添加方法,不管是原生实例还是你自定义子类的实例都能识别到该方法。
可行解决方案
- 方案1:改用组合模式替代继承(最推荐)
不直接继承FileHandle,而是在CSVFile中持有一个FileHandle类型的属性,所有文件操作都通过该属性完成,避免类簇子类化的各种坑。示例代码如下:class CSVFile { var fileName: String private var fileHandle: FileHandle? init(fileName: String) { self.fileName = fileName } func uniqueSuffix() -> String { let now = Date().timeIntervalSinceReferenceDate return String(Int(now)) } func openFile() { let suffix = uniqueSuffix() let docDir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first! let csvURL = docDir.appendingPathComponent(fileName+"_"+suffix).appendingPathExtension("csv") do { FileManager.default.createFile(atPath: csvURL.path, contents: nil, attributes: nil) fileHandle = try FileHandle(forWritingTo: csvURL) print("opened file: \(csvURL.path)") } catch { print("Cannot open \(csvURL)\nError: \(error)") exit(1) } } func writeSamples(pkgIDs: [Double], timestamps: [Double], markers: [Double], samples: [[Double]]) { guard let fileHandle = fileHandle else { return } for iSample in 0..<timestamps.count { let pkgID = String(format: "%d", Int(pkgIDs[iSample])) let tStamp = String(format: "%.6f", timestamps[iSample]) let marker = String(format: "%d", Int(markers[iSample])) var sampleString = pkgID + "," + tStamp + "," + marker for iChannel in 0..<samples.count { sampleString += "," + String(format: "%.5f", samples[iChannel][iSample]/24.0) } sampleString += "\n" fileHandle.write(Data(sampleString.utf8)) } } } - 方案2:保留扩展实现
如果你不需要对CSV文件做额外的状态管理,只是需要给FileHandle添加写CSV采样数据的能力,直接用扩展实现是可行的,注意不要和其他业务的扩展方法重名即可。 - 方案3:修改现有子类调用逻辑
如果你坚持保留现有的子类设计,就不要使用openFile的返回值调用方法,直接用CSVFile实例调用writeSamples,同时修改openFile逻辑,把创建的FileHandle实例赋值给当前子类的内部属性,写操作通过内部持有的实例完成,不要直接使用self.write(因为子类化的FileHandle本身并没有绑定到实际的文件句柄)。
内容的提问来源于stack exchange,提问者Scott T. Miller
相关产品推荐
相关产品推荐

