Swift NSSet & CoreData

匿名 (未验证) 提交于 2019-12-03 08:46:08

问题:

I am trying to move an objective C & CoreData app to Swift and iOS and hit a brick wall with iterating through NSSet objects:

Xcode has generated these classes:

class Response: NSManagedObject {     @NSManaged var responseText: String     @NSManaged var score: NSNumber     @NSManaged var answers: NSSet     @NSManaged var question: Question }  class Question: NSManagedObject {     @NSManaged var questionText: String     @NSManaged var questionType: String     @NSManaged var qualifier: Qualifier     @NSManaged var responses: NSSet } 

then in the 3rd class I am adding a method that needs to iterate though an NSSet..

class Answer: NSManagedObject {      @NSManaged var evaluation: Evaluation     @NSManaged var response: Response      func scoreGap() -> Int {         var max = 0         for aresponse in self.response.question.responses {              // this next line throws an error             var scoreVal = aresponse.score.integerValue              if scoreVal > max { max = scoreVal }         }         return max - self.response.score.integerValue     } } 

The line after the comment above gives and error "AnyObject does not have a member named score"

The Objective-C method looks like this and works fine:

// returns the gap between the score of this answer and the max score -(NSNumber *)scoreGap {     long max = 0;     for(Response *aresponse in self.response.question.responses) {         if(aresponse.score.longValue > max) max = aresponse.score.longValue;     }        max = max - self.response.score.longValue;     return @(max); } 

This is about day three on Swift so I may well be missing something really obvious... but at the moment I really don't get it !

回答1:

for aresponse in self.response.question.responses {     // this next line throws an error     var scoreVal = aresponse.score.integerValue 

The problem is that for aresponse in ... responses is iterating through an NSSet. Swift doesn't know what is in the NSSet, so it treats each value aresponse as an AnyObject, which has no properties. Thus, the compiler complains when you try to get the score property of aresponse.

You, on the other hand, know that each value aresponse is a Response. So you need to tell Swift this, by casting (with as). Note that you were in fact doing that in your Objective-C code:

for(Response *aresponse in self.response.question.responses) 

But you have forgotten to do the same thing in Swift. You can do it in various ways; for example you could write this sort of thing:

for aresponse in self.response.question.responses {     var scoreVal = (aresponse as Response).score.integerValue 

That gets us past the compiler, which now shares in our knowledge of what this variable really is.

Once you get used to this, you will be kissing the ground that Swift walks on. All this time, in Objective-C, you were making assumptions about what kind of object you had, but in fact what you had was an id and the compiler was letting you send any message at all to it - and so, you would often crash with "unrecognized selector". In Swift, an "unrecognized selector" crash is basically impossible because the type of everything is specified and the compiler won't let you send an inappropriate message to an object in the first place.



回答2:

Actually I've discovered something that might be useful. As of version Xcode 7.2 and Swift 2.0 the following declaration works:

@NSManaged var responses: Set<Response> 

I think it might be related with the fact that Swift automatically bridges between NSSet and NSArray. I haven't found this documented but it's working for me and I would appreciate if someone confirms it.



标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!