NSZombies едят мозг моего приложения!

Я отговорил бы от препроцессора (ab) использование, чтобы попытаться сделать синтаксис C больше как этот еще большего количества объектно-ориентированного языка. На наиболее базовом уровне Вы просто используете простые структуры в качестве объектов и раздаете их указателями:

struct monkey
{
    float age;
    bool is_male;
    int happiness;
};

void monkey_dance(struct monkey *monkey)
{
    /* do a little dance */
}

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

struct base
{
    /* base class members */
};

struct derived
{
    struct base super;
    /* derived class members */
};

struct derived d;
struct base *base_ptr = (struct base *)&d;  // upcast
struct derived *derived_ptr = (struct derived *)base_ptr;  // downcast

Для получения полиморфизма (т.е. виртуальные функции) Вы используете указатели функции и дополнительно таблицы указателя функции, также известные как виртуальные таблицы или vtables:

struct base;
struct base_vtable
{
    void (*dance)(struct base *);
    void (*jump)(struct base *, int how_high);
};

struct base
{
    struct base_vtable *vtable;
    /* base members */
};

void base_dance(struct base *b)
{
    b->vtable->dance(b);
}

void base_jump(struct base *b, int how_high)
{
    b->vtable->jump(b, how_high);
}

struct derived1
{
    struct base super;
    /* derived1 members */
};

void derived1_dance(struct derived1 *d)
{
    /* implementation of derived1's dance function */
}

void derived1_jump(struct derived1 *d, int how_high)
{
    /* implementation of derived 1's jump function */
}

/* global vtable for derived1 */
struct base_vtable derived1_vtable =
{
    &derived1_dance, /* you might get a warning here about incompatible pointer types */
    &derived1_jump   /* you can ignore it, or perform a cast to get rid of it */
};

void derived1_init(struct derived1 *d)
{
    d->super.vtable = &derived1_vtable;
    /* init base members d->super.foo */
    /* init derived1 members d->foo */
}

struct derived2
{
    struct base super;
    /* derived2 members */
};

void derived2_dance(struct derived2 *d)
{
    /* implementation of derived2's dance function */
}

void derived2_jump(struct derived2 *d, int how_high)
{
    /* implementation of derived2's jump function */
}

struct base_vtable derived2_vtable =
{
   &derived2_dance,
   &derived2_jump
};

void derived2_init(struct derived2 *d)
{
    d->super.vtable = &derived2_vtable;
    /* init base members d->super.foo */
    /* init derived1 members d->foo */
}

int main(void)
{
    /* OK!  We're done with our declarations, now we can finally do some
       polymorphism in C */
    struct derived1 d1;
    derived1_init(&d1);

    struct derived2 d2;
    derived2_init(&d2);

    struct base *b1_ptr = (struct base *)&d1;
    struct base *b2_ptr = (struct base *)&d2;

    base_dance(b1_ptr);  /* calls derived1_dance */
    base_dance(b2_ptr);  /* calls derived2_dance */

    base_jump(b1_ptr, 42);  /* calls derived1_jump */
    base_jump(b2_ptr, 42);  /* calls derived2_jump */

    return 0;
}

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

Другая (хитрая) вещь, которую можно сделать, изменить динамический тип объекта во времени выполнения! Вы просто повторно присваиваете ему новый vtable указатель. Можно даже выборочно изменить некоторые виртуальные функции при хранении других, создании новых гибридных типов. Просто старайтесь создать новое vtable вместо того, чтобы изменить глобальное vtable, иначе Вы будете случайно влиять на все объекты данного типа.

6
задан Meltemi 30 July 2009 в 00:37
поделиться

3 ответа

Я считаю, что обратная трассировка - это как раз то место, где зомби получает сообщение. Эта обратная трассировка обычно не дает вам никакой информации о причинах сбоя. Он в значительной степени только сообщает вам тип и адрес объекта, который подвергается чрезмерному выпуску.

Я часто использую технику для отслеживания избыточных выпусков, подобную этой, - это использование ObjectAlloc от Instruments для отслеживания всех удержаний и выпусков. Найдите адрес для объекта с избыточным освобождением в ObjectAlloc, затем перечислите все вызовы сохранения / освобождения и затем попытайтесь сбалансировать каждое сохранение с выпуском. Если вы найдете выпуск без соответствующего удержания, значит, проблема обнаружена.

6
ответ дан 10 December 2019 в 00:42
поделиться

Вы можете быстро установить символическую точку останова на objc_exception_throw . Это заставит вашу программу останавливаться при возникновении исключения. Это может не помочь вам отследить , а именно , который CALayer приносит вам горе, но он должен помочь вам найти общее место, где он вызывается.

2
ответ дан 10 December 2019 в 00:42
поделиться

«Впереди неприятности» - это часть предупреждения, а не средство выбора. Само предупреждение исходит от NSInvocation, но тот факт, что в нем упоминается «class _NSZombie_CALayer», означает, что что-то пытается работать с освобожденным CALayer.

Трассировка стека указывает, что это происходит, когда уровень пытается освободить свои подслои .

В целом это означает, что высвобождаемый уровень имеет подслой, который где-то в вашем коде был чрезмерно высвобожден. Проверьте управление памятью CALayers или попробуйте Clang Static Analyzer .

2
ответ дан 10 December 2019 в 00:42
поделиться
Другие вопросы по тегам:

Похожие вопросы: