как использовать директиву #undef в Swift 2.3

Я хочу отменить или переопределить NSLocalizedString в Swift 2.3, и я много искал об этом, и, наконец, я нашел способ в Objective C сделать это, как показано ниже.

#undef NSLocalizedString
#define NSLocalizedString(key,_comment) your_function_name

Я хочу добиться этой функциональности быстро. Я только знаю, что NSLocalizedString - это макрос в NSBundle.h для цели C. Поэтому мы можем переопределить его. Но для быстрого мы не можем этого добиться. Я просто хочу переопределить или переопределить функцию NSLocalizedString для быстрого. Пожалуйста, помогите мне разобраться с этим. Любой помощь будет оценена.


person Sumit Dhariwal    schedule 01.11.2017    source источник
comment
вы считали метод swizzling? Вот пример того, как это сделать в Swift kostiakoval.github.io/posts /methods-swizzling-in-swift   -  person Samer Murad    schedule 08.11.2017
comment
мы можем использовать swizzling для класса и метода экземпляра. Но как вы можете swizzling глобальные функции, NSLocalizedString похожа на глобальную функцию в Swift, которая может вызывать без какой-либо ссылки на класс или объект.   -  person Sumit Dhariwal    schedule 10.11.2017
comment
Это требование очень необычно. Чего вы пытаетесь достичь?   -  person Daniel    schedule 10.11.2017
comment
ты прав, мой плохой, полностью пропустил эту часть   -  person Samer Murad    schedule 10.11.2017


Ответы (3)


NSLocalicedString — это глобальный метод в Swift.

Глобальную функцию нельзя переопределить, но ее можно переопределить. Чтобы переопределить метод, просто объявите новую версию. Выбранная функция будет зависеть от объема. Например:

func NSLocalizedString(_ key: String, tableName: String? = nil, bundle: Bundle = Bundle.main, value: String = "", comment: String) -> String {
    return "redefined version in the project"
}

class Test {
    func NSLocalizedString(_ key: String, tableName: String? = nil, bundle: Bundle = Bundle.main, value: String = "", comment: String) -> String {
        return "redefined version in Test"
    }

    func getLocalizedString() -> String{
        return NSLocalizedString("test", comment: "t")
    }
}

class Test2 {
    func NSLocalizedString(_ key: String, tableName: String? = nil, bundle: Bundle = Bundle.main, value: String = "", comment: String) -> String {
        return "redefined version in Test2"
    }

    func getLocalizedString() -> String{
        return NSLocalizedString("test", comment: "t")
    }
}

NSLocalizedString("test", comment: "t") //will return "redefined version in the project"
Test().getLocalizedString() //will return "redefined version in Test"
Test2().getLocalizedString() //will return "redefined version in Test2"
person Daniel    schedule 09.11.2017
comment
Где вы будете переопределять новую версию в проекте - person Sumit Dhariwal; 10.11.2017
comment
это глобальная функция или метод протокола или что-то еще. - person Sumit Dhariwal; 10.11.2017
comment
и мне нужно переопределить его в каждом классе, где я уже вызываю этот метод - person Sumit Dhariwal; 10.11.2017
comment
ваше решение также не работает, когда мы вызываем метод NSLocalizedString в замыканиях и инициализаторах свойств, вам нужно передать ссылку на этот класс для вызова этого метода. - person Sumit Dhariwal; 10.11.2017
comment
@SumitDhariwal Вы определяете новую версию в проекте вне какого-либо класса. Это глобальная функция. Если вы хотите использовать новую реализацию, вам придется переопределить ее в классах (только если нужна новая реализация, иначе она просто возьмет ближайшую по области видимости). Решение также работает в замыканиях, оно берет функции, наиболее близкие к области, в которой был определен блок. - person Daniel; 10.11.2017
comment
я уже пробовал это решение, оно показывает ошибку, когда мы вызываем функцию NSLocalizedString в инициализаторе свойства, вот описание ошибки: «Невозможно использовать член экземпляра NSLocalizedString» в инициализаторе свойства; инициализаторы свойств запускаются до того, как «я» станет доступным - person Sumit Dhariwal; 13.11.2017
comment
Кроме того, когда мы вызываем эту глобальную функцию в замыканиях, она также показывает следующую ошибку: вызов метода «NSLocalizedString» в замыкании требует явного «я». сделать семантику захвата явной. - person Sumit Dhariwal; 13.11.2017
comment
Надеюсь, теперь вы понимаете реальную проблему. - person Sumit Dhariwal; 13.11.2017
comment
@SumitDhariwal Эти ошибки не связаны с моим ответом. Если вы объясните, чего вы пытаетесь достичь, возможно, я смогу помочь. (Кстати, первую ошибку можно преодолеть, не вызывая self в свойстве, которое инициируется перед self, вторая проблема требует вызова функции с self. перед NSLocalizedString) - person Daniel; 13.11.2017
comment
Кстати. Я не думаю, что вам следует менять реализацию NSLocalizedString. Если вы объясните свою проблему, вероятно, есть лучшее решение. - person Daniel; 13.11.2017

Нашел это, это будет полезно только вам, если вы собираетесь использовать NSLocalizedString в побочных классах. протестировал его на Swift 4 со следующими настройками

extension NSObject {
    func NSLocalizedString(_ key: String, comment: String) -> String {
        return "My custom localization"
    }

    static func NSLocalizedString(_ key: String, comment: String) -> String {
        return "My custom localization"
    }
}
class ViewController: UIViewController {

    @IBOutlet weak var myLabel: UILabel!
    override func viewDidLoad() {
        super.viewDidLoad()
        myLabel.text = NSLocalizedString("mystring",comment:"")
    }
}

должно работать так же для swift 2.3 (без подчеркивания перед параметром key)

Обновлять

Это требует обходного пути при работе с замыканиями, потому что Swift будет ожидать, что вы будете использовать NSLocalizedString с self вот так, self.NSLocalizedString("mystring",comment:""). Обходной путь в этом сценарии — присвоить строку let/var вне замыкания.

Пример:

 // Doesn't work
 self.present(anotherVc, animated: true) { [weak self] in
      self?.myLabel.text = NSLocalizedString("mystring", comment: "") // compiler will throw an error
 } 
 // Does work
 let string = NSLocalizedString("mystring", comment: "")
 self.present(anotherVc, animated: true) { [weak self] in
      self?.myLabel.text = string 
 } 

Для инициализаторов свойств компилятор будет использовать статическую функцию, поэтому важно установить их обе.

Пример:

class MyClass: NSObject {
   // This let
   let myStr = NSLocalizedString("mystring",comment:"")
}

extension NSObject {
    func NSLocalizedString(_ key: String, comment: String) -> String {
        return "My custom localization"
    }
    // Uses the static method    
    static func NSLocalizedString(_ key: String, comment: String) -> String {
        return "My custom localization"
    }
}
person Samer Murad    schedule 10.11.2017
comment
попробуйте вызвать NSLocalizedString в инициализаторе свойства и закрытии. - person Sumit Dhariwal; 13.11.2017
comment
вы имеете в виду, что потребность в self нарушает поведение, которого вы пытаетесь достичь? вы всегда можете назначить нужную строку let вне замыкания и использовать let внутри: let string = NSLocalizedString("mystring", comment: ""); self.present(anotherVc, animated: true) { [weak self] in self?.myLabel.text = string } - person Samer Murad; 13.11.2017
comment
для инициализаторов свойств компилятор использует статическую функцию - person Samer Murad; 13.11.2017
comment
Вы имеете в виду, что потребность в себе ломает поведение, которого вы пытаетесь достичь? - Да. обновите свой ответ, что вы пытаетесь предложить. - person Sumit Dhariwal; 13.11.2017
comment
и вы не можете установить строку за пределами замыкания из-за использования сторонней библиотеки? или это просто слишком много накладных расходов? - person Samer Murad; 13.11.2017

#undef — это директива препроцессора C и Swift не поддерживает любые директивы препроцессора, включая сложный макрос #define, используемый для NSLocalizedString. На самом деле он импортируется как:

/// Returns a localized string, using the main bundle if one is not specified.
public func NSLocalizedString(_ key: String, tableName: String? = default, bundle: Bundle = default, value: String = default, comment: String) -> String

Однако вы могли бы легко реализовать задуманное поведение, просто написав собственную реализацию, которая вместо этого должна использоваться автоматически. Например:

public func NSLocalizedString(_ key: String, tableName: String? = nil, bundle: Bundle = Bundle.main, value: String = "", comment: String) -> String {
    // Do your magic here!
    return "test"
}

Но, пожалуйста, не делай этого. Было бы гораздо лучше просто создать другую функцию. В противном случае не будет ясно, используется ли ваша функция или нет, или вы можете получить: error: ambiguous use of 'NSLocalizedString(_:tableName:bundle:value:comment:)' если вам нужно изменить подпись, даже если вы просто хотите сделать что-то необязательное.

person Coder-256    schedule 03.11.2017
comment
вы имеете в виду создать другую функцию с именем NSLocalizedString для каждого класса - person Sumit Dhariwal; 06.11.2017
comment
Да, это или только одна функция, которая принимает Any, но опять же я рекомендую вам назвать эту функцию как-нибудь иначе и отметить мой ответ как принятый, если он вам помог. - person Coder-256; 06.11.2017
comment
Нет, мне это бесполезно. Я уже знаю, что он может работать с пользовательской функцией, но мне нужно переопределить, снова прочитать мой вопрос - person Sumit Dhariwal; 07.11.2017
comment
Было бы полезно, если бы вы могли предоставить некоторый контекст, например, почему это необходимо и какие именно параметры вы будете передавать. Я спрашиваю только потому, что это очень редкий случай использования. - person Coder-256; 07.11.2017
comment
Scenerio: мы вызываем этот метод более чем в 100 классах, поэтому теперь мы пытаемся переопределить этот метод в одном месте, чтобы справиться с ситуацией отката языка. - person Sumit Dhariwal; 10.11.2017
comment
Хорошо, но что именно ему будет передано? - person Coder-256; 10.11.2017