Метод Swizzling в iOS 5?

Apple заблокировала метод Swizzling в iOS 5?

Я немного поиграл и обнаружил, что приложение с методом Swizzling работает на iOS 4, но не на iOS 5.

ПРИМЕЧАНИЕ. Приложение работает на iOS 5, но не на части, когда используется метод Swizzling.

Ответ 1

Apple отправила электронное письмо некоторое время назад некоторым разработчикам, которые, как было установлено, используют метод swizzling в приложениях App Store:

Ваше приложение, xxx, в настоящее время отправленное в App Store, использует method_exchangeImplementations для обмена реализацией Apple предоставили API с вашими собственными реализациями. Из-за предстоящих изменения, это поведение в вашей заявке может вызвать сбой или вызвать потеря пользовательских данных на iPhone OS 4.0.

xxx использует метод_exchangeImplementations для обмена реализацией dealloc с вашим методом ttdealloc. Он также реализация метода popViewControllerAnimated: с помощью метод popViewControllerAnimated2:.

Пожалуйста, немедленно устраните эту проблему и загрузите новый iTunes Connect. Мы можем удалить вашу заявку, если считаем, что делать это разумно или необходимо.

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

Ответ 2

UPDATE: (Мое приложение использует этот метод и находится в appstore)

Метод swizzling, похоже, работает с 30 мая 2012 года. Это моя реализация.

(Это для тех из вас, кто оглядывается и находит плохой код на страницах вики и просто хочет быстро реализовать.)

Swizz.h

#import <Foundation/Foundation.h>

void ActivateAutoSwizz();

void Swizz(Class c, SEL orig, SEL replace);


@interface NSObject (swizz)

// This Method allows the class to Swizzle more methods within itself.
// And allows for an overridable init method in Class Extensions
// ###################### 
// //// To Swizzle a method, call Swizzle once on the class in question.
// //// dispatch_once is a good way to handle that.
//            static dispatch_once_t onceToken;
//            dispatch_once(&onceToken, ^{
//                Swizz([UITableViewCell class], @selector(reuseIdentifier), @selector(classReuseIdentifier));
//            });
- (void) swizzInit;

@end

Swizz.m

#import "Swizz.h"
#import <objc/runtime.h> 
#import <objc/message.h>
//....

void Swizz(Class c, SEL orig, SEL replace)
{
    Method origMethod = class_getInstanceMethod(c, orig);
    Method newMethod = class_getInstanceMethod(c, replace);
    if(class_addMethod(c, orig, method_getImplementation(newMethod), method_getTypeEncoding(newMethod)))
        class_replaceMethod(c, replace, method_getImplementation(origMethod), method_getTypeEncoding(origMethod));
    else
        method_exchangeImplementations(origMethod, newMethod);
}


@implementation NSObject (swizz)

// Load gets called on every object that has it. Off Thread, before application start.
+ (void) load
{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        Swizz([NSObject class], @selector(init), @selector(initIntercept));
    });
}

- (id) initIntercept{
    self = [self initIntercept]; // Calls the Original init method
    if(self){
        [self swizzInit];
    }
    return self;
}

- (void) swizzInit{
    //Do Nothing.. Gives an extension point for other classes.
}

@end

Я построил это, чтобы позволить мне перехватить идентификатор повторного использования в UITableViewCell с расширением UITableViewCell.

Вот этот пример.

UITableViewCell + ReuseIdentifier.h

#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>

@interface UITableViewCell (ReuseIdentifier)

@end

UITableViewCell + ReuseIdentifier.m

#import "UITableViewCell+ReuseIdentifier.h"
#import "Swizz.h"

@implementation UITableViewCell(ReuseIdentifier)


// Edited to remove Class variable.
// Class variables in Categories are Global.
// And as it turns out, this method did not need the variable anyhow.
- (NSString *)classReuseIdentifier{
    NSString *reuseIdentifierResult = [self classReuseIdentifier]; // Gets the original reuseIdentifier
    if (reuseIdentifierResult == nil){
        reuseIdentifierResult = [[self class] description];
    }
    return reuseIdentifierResult;
}

// Alternatively you can use the +(void)load method on the class to achieve the same thing.
- (void)swizzInit{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        Swizz([UITableViewCell class], @selector(reuseIdentifier), @selector(classReuseIdentifier));
    });
}

@end

Как вы можете видеть, ActivateAutoSwizz(), а также мой метод swizzInit используют dispatch_once для выполнения swizzle один раз.

static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
    Swizz([NSObject class], @selector(init), @selector(initIntercept));
});

Если вы выполните его дважды. он меняет ваш метод обратно на оригинал. Надеюсь, это поможет некоторым из вас разработчиков iOS.

ПРИМЕЧАНИЕ. Я определил, что загрузка + (void) вызывается один раз при запуске приложения и является прекрасным местом для достижения метода swizzle. К сожалению, в некоторых ситуациях dev + (void) загрузка не вызывается, вы можете протестировать свое приложение, чтобы убедиться, что эти методы вызывают.

Ответ 3

Хорошо, мы получили OK о mont hago (начало мая 2012 года) для приложения, которое сильно использовало метод Swizzling для настройки стандартных компонентов пользовательского интерфейса в iOS4 (iOS5 с использованием внешнего вида). Кроме того, метод swizzling - полностью документированный API, который также предоставляет очень мощные функции, не связанные с самим Apple или использованием частных API. Мне трудно поверить, что они могут отказаться от такой вещи!

В любом случае, пожалуйста, сообщите всем, если вы увидите больше отклонений, связанных с этим! Спасибо!