草庐IT

objective-c - 如何将 Objective-C 中的模块移植到 Swift 中?

coder 2023-09-10 原文

在尝试了一些 Swift 小程序后,我决定下一步是将 Objective-C 程序中的单个模块移植到 Swift 中,看看需要哪些步骤。我有很多问题,所以我想我应该在这里发布我的过程和结果,以防其他人发现它有用。

我还创建了一个表格来帮助我记住不同的转换。不幸的是,StackOverflow 不支持表格,因此我将这些转换发布为 Github gist here .

虽然 Apple 无疑会提供 Xcode Refactor 来将 Objective-C 转换为 Swift,但手动转换是熟悉两种语言之间差异的好方法。您熟悉的语言涉及很多“肌肉内存”,这是熟悉新语法的好方法。正如 Apple 所 promise 的那样,事实证明这些语言有很多共同的想法,这主要是一个机械过程(而不是从 C++ 甚至传统的 C 移植)。

请注意,此过程没有使用 Swift 的任何令人兴奋的新功能,它只是直接获取代码。我应该提一下,迁移到 Swift 将限制对 iOS 7 或 OS X 10.9 的任何向后兼容性。我还遇到了几个问题(下面有解决方法),我确定这些问题只是由于项目的第一个测试版发布状态,所以在未来的版本中可能不需要。

我选择了 iPhoneCoreDataRecipes 并选择了一个不依赖很多其他模块的模块:IngredientDetailViewController。如果您想继续,请查看下面我的“答案”。

希望有用

最佳答案

0) 下载项目副本 here并在 Xcode 版本 6 中打开 Recipes.xcodeproj

1) 选择File>New File…>iOS Source>Swift File> IngredientDetailViewController(文件夹:类,组:配方 View Controller )

2) 对“您要配置 Objective-C 桥接 header 吗?”回复"is"

3) 从 Recipes_Prefix.pch 复制下面的前三行,从 IngredientDetailViewController.m 复制下面的三行到 Recipes-Bridging-Header.h。如果你做更多的文件,显然不要重复行,并删除你已经转换为 Swift 的任何文件。考虑到它们已导入到 swift 文件中,我还没有找到任何地方记录对 Cocoa 线的需求,但是......

#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import <CoreData/CoreData.h>
#import "Recipe.h"
#import "Ingredient.h"
#import "EditingTableViewCell.h"

4) 将 IngredientDetailViewController.h 文件和 IngredientDetailViewController.m 文件中的文本复制/粘贴到 IngredientDetailViewController.swift 中。

5) 从项目中删除 IngredientDetailViewController.h.m 文件。

6) 从#import "IngredientDetailViewController.h"#import "Recipes-Swift.h" 进行全局查找和替换(只有一次转换在这种情况下,对于更多文件,请不要在您的 Objective-C 模块中复制这一行。)

7) 检查 Project>Targets>Recipes>Build Settings Runpath Search Paths。如果它显示 $(inherited),请删除此行,否则您将在启动时收到有关“找不到图像”的错误

8) 将 IngredientDetailViewController.swift 中的 Objective-C 语法转换为 Swift。查看GitHub Gist mentioned above所需的替换,或以下我转换后的版本。

9) 您可能需要更新 IB 链接。执行 Find>Find in Files on IngredientDetailViewController 并在 Interface Builder 中选择一个。在右侧栏中打开 Identity Inspector。在类字段中选择 IngredientDetailViewController,键入 xxx 或其他内容,然后按 Tab。

10) 构建并运行。请注意,进入食谱后,您必须点击编辑,然后点击成分的信息按钮以激活 IngredientDetailViewController

12) 恭喜你构建了第一个 Swift/Objective-C 混合程序!

这是我在这个特定模块中的剪辑:

``

class IngredientDetailViewController: UITableViewController {

    var recipe: Recipe!
    var ingredient: Ingredient! {
    willSet {

        if let newIngredient = newValue {
            self.ingredientStr = newIngredient.name
            self.amountStr = newIngredient.amount
        } else {
            self.ingredientStr = ""
            self.amountStr = ""
        }
    }
    }
    init(nibName nibNameOrNil: String!, bundle nibBundleOrNil: NSBundle!) {
        super.init(nibName:nibNameOrNil, bundle: nibBundleOrNil?)
    }
    init(coder aDecoder: NSCoder!) {
        super.init(coder: aDecoder)
    }
    init(style: UITableViewStyle) {
        super.init(style: style)
    }

    // MARK: table's data source
    var ingredientStr: String?
    var amountStr: String?

    // view tags for each UITextField
    let kIngredientFieldTag =    1
    let kAmountFieldTag  = 2



    override func viewDidLoad () {

        super.viewDidLoad()

        self.title = "Ingredient"

        self.tableView.allowsSelection = false
        self.tableView.allowsSelectionDuringEditing = false
    }


    override  func tableView(tableView: UITableView!, numberOfRowsInSection section: Int) -> Int {
        return 2
    }

    override func tableView(tableView: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCell! {

        let IngredientsCellIdentifier = "IngredientsCell"
        let cell = tableView.dequeueReusableCellWithIdentifier(IngredientsCellIdentifier, forIndexPath: indexPath ) as EditingTableViewCell
        if (indexPath.row == 0) {
            // cell ingredient name

            cell.label.text = "Ingredient"
            cell.textField.text = self.ingredientStr
            cell.textField.placeholder = "Name"
            cell.textField.tag = kIngredientFieldTag
        }
        else if (indexPath.row == 1) {

            // cell ingredient amount
            cell.label.text = "Amount"
            cell.textField.text = self.amountStr
            cell.textField.placeholder = "Amount"
            cell.textField.tag = kAmountFieldTag
        }

        return cell
    }



    @IBAction func  save (sender: AnyObject!) {

        if let context = self.recipe.managedObjectContext  {
            if (!self.ingredient) {
                self.ingredient = NSEntityDescription.insertNewObjectForEntityForName("Ingredient",
                    inManagedObjectContext:context) as Ingredient
                self.recipe.addIngredientsObject(self.ingredient)
                self.ingredient.displayOrder = self.recipe.ingredients.count
            }

            // update the ingredient from the values in the text fields
            let cell = self.tableView.cellForRowAtIndexPath(NSIndexPath(forRow:0, inSection:0)) as EditingTableViewCell
            self.ingredient.name = cell.textField.text


            // save the managed object context
            var error: NSError? = nil
            if !context.save( &error)  {
                /*
                Replace this implementation with code to handle the error appropriately.

                abort() causes the application to generate a crash log and terminate.
                You should not use this function in a shipping application, although it may be
                useful during development. If it is not possible to recover from the error, display
                an alert panel that instructs the user to quit the application by pressing the Home button.
                */
                println("Unresolved error \(error), \(error!.userInfo)")
                abort()
            }

        }
        // if there isn't an ingredient object, create and configure one

        self.parentViewController.dismissViewControllerAnimated(true, completion:nil)
    }

    @IBAction func cancel(sender: AnyObject!) {

        self.parentViewController.dismissViewControllerAnimated(true, completion:nil)
    }

    func textFieldDidEndEditing(textField:UITextField) {

        // editing has ended in one of our text fields, assign it's text to the right
        // ivar based on the view tag
        //
        switch (textField.tag)
            {
        case kIngredientFieldTag:
            self.ingredientStr = textField.text

        case kAmountFieldTag:
            self.amountStr = textField.text
        default:
            break
        }
    }
}

关于objective-c - 如何将 Objective-C 中的模块移植到 Swift 中?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24125265/

有关objective-c - 如何将 Objective-C 中的模块移植到 Swift 中?的更多相关文章

  1. ruby - 如何使用 Nokogiri 的 xpath 和 at_xpath 方法 - 2

    我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div

  2. ruby - 如何从 ruby​​ 中的字符串运行任意对象方法? - 2

    总的来说,我对ruby​​还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用

  3. ruby - 为什么我可以在 Ruby 中使用 Object#send 访问私有(private)/ protected 方法? - 2

    类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc

  4. ruby - 在 Ruby 中使用匿名模块 - 2

    假设我做了一个模块如下:m=Module.newdoclassCendend三个问题:除了对m的引用之外,还有什么方法可以访问C和m中的其他内容?我可以在创建匿名模块后为其命名吗(就像我输入“module...”一样)?如何在使用完匿名模块后将其删除,使其定义的常量不再存在? 最佳答案 三个答案:是的,使用ObjectSpace.此代码使c引用你的类(class)C不引用m:c=nilObjectSpace.each_object{|obj|c=objif(Class===objandobj.name=~/::C$/)}当然这取决于

  5. ruby - 其他文件中的 Rake 任务 - 2

    我试图在一个项目中使用rake,如果我把所有东西都放到Rakefile中,它会很大并且很难读取/找到东西,所以我试着将每个命名空间放在lib/rake中它自己的文件中,我添加了这个到我的rake文件的顶部:Dir['#{File.dirname(__FILE__)}/lib/rake/*.rake'].map{|f|requiref}它加载文件没问题,但没有任务。我现在只有一个.rake文件作为测试,名为“servers.rake”,它看起来像这样:namespace:serverdotask:testdoputs"test"endend所以当我运行rakeserver:testid时

  6. ruby-on-rails - Ruby net/ldap 模块中的内存泄漏 - 2

    作为我的Rails应用程序的一部分,我编写了一个小导入程序,它从我们的LDAP系统中吸取数据并将其塞入一个用户表中。不幸的是,与LDAP相关的代码在遍历我们的32K用户时泄漏了大量内存,我一直无法弄清楚如何解决这个问题。这个问题似乎在某种程度上与LDAP库有关,因为当我删除对LDAP内容的调用时,内存使用情况会很好地稳定下来。此外,不断增加的对象是Net::BER::BerIdentifiedString和Net::BER::BerIdentifiedArray,它们都是LDAP库的一部分。当我运行导入时,内存使用量最终达到超过1GB的峰值。如果问题存在,我需要找到一些方法来更正我的代

  7. python - 如何使用 Ruby 或 Python 创建一系列高音调和低音调的蜂鸣声? - 2

    关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。

  8. ruby-on-rails - Rails 3 中的多个路由文件 - 2

    Rails2.3可以选择随时使用RouteSet#add_configuration_file添加更多路由。是否可以在Rails3项目中做同样的事情? 最佳答案 在config/application.rb中:config.paths.config.routes在Rails3.2(也可能是Rails3.1)中,使用:config.paths["config/routes"] 关于ruby-on-rails-Rails3中的多个路由文件,我们在StackOverflow上找到一个类似的问题

  9. ruby-on-rails - 如何验证 update_all 是否实际在 Rails 中更新 - 2

    给定这段代码defcreate@upgrades=User.update_all(["role=?","upgraded"],:id=>params[:upgrade])redirect_toadmin_upgrades_path,:notice=>"Successfullyupgradeduser."end我如何在该操作中实际验证它们是否已保存或未重定向到适当的页面和消息? 最佳答案 在Rails3中,update_all不返回任何有意义的信息,除了已更新的记录数(这可能取决于您的DBMS是否返回该信息)。http://ar.ru

  10. ruby-on-rails - 'compass watch' 是如何工作的/它是如何与 rails 一起使用的 - 2

    我在我的项目目录中完成了compasscreate.和compassinitrails。几个问题:我已将我的.sass文件放在public/stylesheets中。这是放置它们的正确位置吗?当我运行compasswatch时,它不会自动编译这些.sass文件。我必须手动指定文件:compasswatchpublic/stylesheets/myfile.sass等。如何让它自动运行?文件ie.css、print.css和screen.css已放在stylesheets/compiled。如何在编译后不让它们重新出现的情况下删除它们?我自己编译的.sass文件编译成compiled/t

随机推荐