Главная iPhone Mac OS X Форум О себе

Правила преобразования кода в PyObjC

PyObjC — это бридж, мостик, связующая библиотека между Python и Objective-C. Он позволит вам писать приложения на Питоне, которые используют и расширяют функционал существующих библиотек и классов Objective-C, что не мало важно, в том числе и библиотеки Cocoa. Apple включала в состав XCode бриджи начиная с Mac OS 10.0, когда предоставила возможность писать приложения с использованием Java, в 10.4 — JavaScript (для виджетов). На данный момент существуют бриджи для многих языков, но в стандартную поставку с XCode включены бриджи для Java, JavaScript, Python и Ruby, снабженные примерами использования этих языков.

Так как вся документация и примеры кода на developer.apple.com (этакий MSDN для Mac OS) написана для Objective-C, то нам потребуется соблюдать ряд правил преобразования кода, чтобы он работал с использованием Python.

  1. Правило преобразования имен методов

    Объекты в Objective-C общаются между собой путем отправки сообщений. Для посылки сообщений используется следующий синтаксис:

    
    [receiver message];
    
    

    где receiver — указатель на объект, а message — имя вызываемого метода. После преобразования в код питона это будет выглядеть следующим образом:

    
    receiver.message()
    
    

    Примеры преобразования для разных случаев исходного кода:

    
    # Эквивалент для метода принимающего один параметр:
    #
    # [helloString stringByAppendingString:@"world"];
    #
    helloString.stringByAppendingString_(u"world")
    
    # При передаче множества параметров:
    #
    # [myObject makeGroup: obj1, obj2, obj3];
    #
    myObject.makeGroup_(obj1, obj2, obj3)
    
    # Для именованных параметров название процедуры в Питоне будет складываться
    # из имени метода и имен параметров с добавлением нижнего подчеркивания
    # к имени каждого из них.
    #
    # [myRect setWidth:10 height:20];
    #
    receiver.setWidth_height_(10, 20)
    
    
  2. Правило работы с NSError**

    NSError — класс, который предоставляет доступ к расширенной информации об ошибках. Некоторые функции принимают параметр этого типа в вызове своих методов, как в приведенном ниже примере, и возвращают указатель на объект NSError, если произошла ошибка, или nil (эквивалент None в Python), если функция отработала без проблем.

    
    NSError *error;
    NSString *names = [NSString stringWithContentsOfFile:@"/usr/share/dict/propernames"
                                                encoding:NSASCIIStringEncoding
                                                   error:&error];
    if(error == nil) {
        NSLog(@"Successfully read names: %@", names);
    } else {
        NSLog(@"Encountered an error attempting to read names: %@", [error description]);
    }
    
    

    К сожалению, PyObjC не может корректно обработать подобные вызовы. Поэтому в случае вызова метода, берущего параметр типа NSError**, опускайте его в своем коде. PyObjC самостоятельно отследит использование объекта NSError, динамически создаст его и приложит к возвращаемым результатам метода в виде кортежа.

    
    names, error = NSString.stringWithContentsOfFile_encoding_error_(
        u"/usr/share/dict/propernames",
        NSASCIIStringEncoding)
    if(error == None):
        NSLog(u"Successfully read names: "+names)
    else:
        NSLog(u"Encountered an error attempting to read names: "+error.description())
    
    
  3. Создание экземпляров объектов

    Создание экземпляра объекта (а все объекты в Mac OS являются производными от NSObject) отличается от привычных способов создания объекта в Питоне:

    
    myPythonObject = MyPythonClass()
    
    

    Создание нового объекта разбивается на два шага: выделение памяти и инициализация объекта. Первый шаг реализуется методом alloc, который выделяет необходимое количество памяти, и возвращает указатель на выделенную под объект память. Вторая часть — вызовом метода инициализации, обычно init (обратите внимание, это обычный, ничем не отличающийся от других, метод).

    Довольно часто у объектов бывает сразу несколько методов, начинающихся с init, например init, initWithString, initWIthContentsOfFile и т.д. Установившейся практикой в таком случае является выделение среди всех init-методов одного, называемого designated initializer. Все остальные init-методы должны вызывать его и только он вызывает унаследованный init метод.

    Создание объекта.

    
    myString = NSString.alloc().init()
    
    

    Многие Objective-C классы содержат методы, выполняющие двухпроходную инициализацию за вас. Например:

    
    # Эквивалент:
    #
    # myObject = NSObject.alloc().init()
    #
    myObject = NSObject.new()
    
    # Эквивалент:
    #
    # myDict = NSDictionary.alloc().init()
    #
    myDict = NSDictionary.dictionary()
    
    # Эквивалент:
    #
    # myString = NSString.alloc().initWithString_(u'my string')
    #
    myString = NSString.stringWithString_(u'my string')
    
    
    

    Более подробную информацию о существующих для объекта методах инициализации можно найти в документации.

    Вы также должны соблюдать данное соглашение при создании производных от Objective-C классов. В своем конструкторе вы должны явно вызвать инициализирующую функцию класса, который вы наследуете.

    
    class MyClass(NSObject):
    
        def init(self):
            # В оригинальной документации, поставляемой с XCode для PyObjC
            # вас просят инициализировать родительский класс вызовом
            # 
            # self = super(MyClass, self).init()
            # 
            # Как я понял, документация была написана года два назад, когда
            # у Питона не было нормально реализовано наследование классов и
            # встречались такие извращения при инициализации как это:
            # 
            # class A:
            #    def method(self):
            #      print "Hi: A" 
            #  class B(A):
            #    __baseclass = A
            #    def method(self):
            #      print "Hi: B" 
            #      self.__baseclass.method(self)
            # 
            # Посему предлагаю использовать стандартный для Питона
            # способ вызова родительского конструктора
    
            self = NSObject.init(self)
            if self is None: return None
    
            self.myVariable = 10
            
            # В отличие от конструктора класса в Питоне инициализатор
            # должен возвращать self
            return self
    

В более глубокие детали программирования и рекомендации по построению кода на Питоне под Маком мы погрузимся в последующих постах.

Комментарий

Комментарии

ZYV 19.07.2020 23:24

Жень, http://www.zoitz.com/archives/36 видел? :)

ответить

Форма комментирования для «Правила преобразования кода в PyObjC»

Обязательное поле. Не больше 30 символов.

Обязательное поле

captcha image Пожалуйста, введите символы, которые вы видите на изображении

.