Как получить инициализированный SELF из статического метода init - PullRequest
0 голосов
/ 02 ноября 2019

В приведенном ниже коде я пытаюсь использовать метод init, который может вызываться статически, поскольку считаю, что метод инициализации должен быть обязанностью класса.

Однако в приведенном ниже коде .h,.m и основные файлы, при компиляции приложение аварийно завершает работу и получает следующее сообщение:

*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** +[Synchron<0x1064540b0> init]: cannot init a class     object.'

Пожалуйста, дайте мне знать, как решить эту ошибку и почему я ее получаю.

main

#import <UIKit/UIKit.h>
#import "AppDelegate.h"
#import "Synchron.h"

int main(int argc, char * argv[]) {
    @autoreleasepool {

    [Synchron initSelfWithName:@"XYZ" andId:@"000"];
    return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
    }
}

.m :

@implementation Synchron
@synthesize SynchronName;   
@synthesize SynchronId;

+(id)initSelfWithName:(NSString *)synchName andId:(NSString *)synchId {
    Synchron *synch;
    if (self == [super init]) {
    synch = [[Synchron alloc] init];
    synch.SynchronName = synchName;
    synch.SynchronId = synchId;
    NSLog(@"synch: %@", synch.SynchronName);
    NSLog(@"synch: %@", synch.SynchronId);
    }
    return synch;
}
@end

.h

@interface Synchron : NSObject {

}

@property (weak, nonatomic) NSString *SynchronName;
@property (weak, nonatomic) NSString *SynchronId;

+(id) initSelfWithName: (NSString *) synchName andId: (NSString *) synchId;

@end

Ответы [ 4 ]

1 голос
/ 03 ноября 2019

Есть несколько проблем с вашим кодом.

  1. Использование @synthesize для стандартных свойств давно устарело.
  2. Ваши свойства должны быть strong, а неweak.
  3. Инициализатор класса не должен начинаться с init.
  4. self в методе класса представляет класс, а не экземпляр класса.
  5. Имена свойств должны начинаться со строчных букв.

Ваш обновленный код будет выглядеть следующим образом:

.h

@interface Synchron : NSObject

@property (strong, nonatomic) NSString *name;
@property (string, nonatomic) NSString *id;

+ (instancetype)synchronWithName:(NSString *)name andId:(NSString *)id;

@end

.m

@implementation Synchron

+ (instancetype)synchronWithName:(NSString *)name andId:(NSString *)id {
    Synchron *synch = [[self alloc] init];
    synch.name = name;
    synch.id = id;

    return synch;
}

@end

Теперь вы можете использовать его как:

Synchron *val = [Synchron synchronWithName:@"XYZ" andId:@"000"];
1 голос
/ 03 ноября 2019

Вы вызываете self в методе класса. Таким образом, self относится к class, , а не и object. class не может быть инициализирован как [super init].

Когда вы вызываете [[Synchron alloc] init], он состоит из двух частей!

  1. [Synchron alloc] возвращает выделенный объект
  2. [allocatedObject init] возвращает инициализированный объект

Так что вызов [Synchron init] - нонсенс.

Инициализация это не ответственность класса! Только выделение - это!

Итак:

- (id)initWithName:(NSString *)synchName andId:(NSString *)synchId {
    self = [super init]; // Here `self` refers to an object, not the class. Because we are inside an instance method.
    if(self) {
        self.SynchronName = synchName;
        self.SynchronId = synchId;
    }
    return self;
}

Тогда у вас также может быть метод класса построителя экземпляров, например:

+ (instancetype)synchronWithName:(NSString *)synchName andId:(NSString *)synchId {
    return [[self alloc] initWithName: synchName andId: synchId];
}
0 голосов
/ 04 ноября 2019

Пара наблюдений:

  • Соглашение заключается в объявлении init метода экземпляра, который используется вместе с alloc, например,

    Synchron *synchron = [[Synchron alloc] initWithName:name identifier:identifier];
    
  • Если вы хотите, вы также можете определить метод вспомогательного класса для создания экземпляра, хотя вы обычно не используете префикс init для этого имени метода. Обычно вы используете префикс имени класса, например synchron:

    Synchron *synchron = [Synchron synchronWithName:name identifier:identifier];
    

    Вы также определите этот метод, чтобы он возвращал instancetype вместо id, поэтому система проверки типовбудет автоматически знать, какой тип он возвращает (и, используя instancetype вместо явного использования Synchron *, он работает и для подклассов).

    Как указала CRD, вы также можете использовать префикс newпоэтому вместо synchronWithName:identifier: это может быть newWithName:identifier:. Это встречается не так часто, но приводит к немного отличному поведению ARC в некоторых крайних случаях, но здесь это несущественно.

  • Незначительные наблюдения, но мы часто определяем эти свойства так, что:

    • имена свойств начинаются со строчной буквы;
    • мы используем семантику copy, чтобы избежать проблем с передачей NSMutableString и его мутацией за нашими спинами;и
    • обратите внимание, что в реализации synchronWithName я использую синтаксис [[self alloc] init] вместо [[Synchron alloc] init], что обеспечивает правильное поведение, если мы когда-либо создадим подкласс Synchron.

Таким образом, потянув это в целом:

@interface Synchron: NSObject

@property (copy, nonatomic) NSString *name;
@property (copy, nonatomic) NSString *identifier;

- (id)initWithName:(NSString *)name identifier:(NSString *)identifier;
+ (instancetype)synchronWithName:(NSString *)name identifier:(NSString *)identifier;

@end

@implementation Synchron

- (id)initWithName:(NSString *)name identifier:(NSString *)identifier {
    self = [super init];
    if (self) {
        self.name = name;
        self.identifier = identifier;
    }
    return self;
}

+ (instancetype)synchronWithName:(NSString *)name identifier:(NSString *)identifier {
    return [[self alloc] initWithName:name identifier:identifier];
}

@end

Вы говорите:

Я пытаюсь использовать метод init, который можно вызывать статически, потому что считают, что метод инициализации должен быть ответственностью класса.

По сути, распределение - это ответственность класса, но инициализация - это ответственность экземпляра. Но, как показано выше, вполне приемлемо предложить разработчику метод удобного класса, который соответствует как alloc, так и init.

.
0 голосов
/ 03 ноября 2019

В приведенном ниже коде я пытаюсь использовать метод init, который можно вызывать статически, поскольку считаю, что метод инициализации должен быть ответственностью класса.

Существуетопределенное «семейство» методов для методов инициализации класса, так же, как методы инициализации экземпляра начинаются с init, методы класса начинаются с new. Существует реализация по умолчанию new и ее документация состояний:

Этот метод представляет собой комбинацию alloc и init. Как и alloc, он инициализирует переменную экземпляра isa нового объекта, поэтому он указывает на структуру данных класса. Затем он вызывает метод init для завершения процесса инициализации.

Чтобы определить свой метод, следуйте тому же шаблону, который вы обычно используете для определения пользовательского метода init: вы вызываете его new... и вызовите new для создания объекта, который вы специализируете:

+(instancetype) newWithName:(NSString *)synchName andId:(NSString *)synchId
{
   Synchron *synch;

   synch = [self new];  // any call to new or alloc/init may return nil
   if (synch)               // so check before setting the properties
   {
      synch.synchronName = synchName;
      synch.synchronId = synchId;
   }
   return synch;
}

Примечания: вышеизложенное изменило три вещи по сравнению с вашим кодом

  1. Имена свойств начинаются со строчной буквы в соответствии со стандартным соглашением об именах.

  2. Тип возвращаемого значения был изменен на instancetype. Это новое ключевое слово было добавлено в Objective-C для повышения безопасности типов. Он используется, когда метод возвращает экземпляр своего класса, например, методы инициализации и фабрики, и позволяет лучше предупреждать компилятор. Подробности его точного значения см. В разделе instancetype в Принятие Modern Objective-C . Вы все равно увидите, что id используется много.

  3. self используется вместо Synchron в вызове new. В методе класса self относится к классу (а не к экземпляру, как в методе экземпляра), к которому был вызван метод. Это изменение поддерживает случай, когда метод был вызван из подкласса Synchron и, следовательно, self относится к этому подклассу.

Кредит: изменения 2 и 3 былиПервоначально опущено, чтобы упростить ответ, но комментарии @Rob к нескольким ответам побудили нас включить их для полноты. (Ваш код «работает» без этих изменений.)

Этот метод имеет ту же семантику возврата, что и alloc / init, и возвращаемое значение принадлежит вызывающей стороне и ееВремя жизни будет управляться ARC (автоматический подсчет ссылок). Подробнее о семействах методов см. В разделе Семейства методов в документации ARC.

Эра до ARC

В эру до ARCкогда вам приходилось вручную управлять временем жизни объекта - известным как MRR (ручное сохранение выпуска) или MRC (ручной подсчет ссылок), семейство методов new использовалось меньше. Вместо фабричных методов в соответствии с соглашением об именах <classname>..., в вашем случае этот метод был бы следующим:

+(id) synchronWithName:(NSString *)synchName andId:(NSString *)synchId

Такой метод не входит в семейства new или init и поэтому возвращает неизвестную ссылкувызывающему абоненту, и ARC должен справиться с этим немного по-другому. Важно: , поскольку его название подразумевает ARC, Автоматический Подсчет ссылок, будет обрабатывать такой фабричный метод без необходимости беспокоиться о различной семантике возвращаемого значения, однако кодпроизведенный будет очень незначительно менее эффективен.

Тело фабричного метода может быть точно таким же, как у new семейного метода, хотя было бы более распространенным использовать alloc/ init для создания объекта:

+(instancetype) synchronWithName:(NSString *)synchName andId:(NSString *)synchId
{
   Synchron *synch;

   synch = [[self alloc] init];
   if (synch)
   {
      synch.synchronName = synchName;
      synch.synchronId = synchId;
   }
   return synch;
}

Pick Your Style

Вы увидите множество фабричных методов <classname>... вокруг и, вероятно, довольнонесколько меньше методов инициализации класса new.... ARC обрабатывает и автоматически, и разница в эффективности, скорее всего, будет незначительной для вашего кода ( никогда преждевременно оптимизировать, т. Е. Не тратить на это время, если у вас нет проблем - но в то же время нетнапишите намеренно плохо исполняемый код!).

Независимо от того, используете ли вы заводской стиль или семейный стиль new, нет правильного или неправильного.

HTH

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...