[notas de estudo do iOS] - fale sobre biblioteca estática e biblioteca dinâmica

[notas de estudo do iOS] - fale sobre biblioteca estática e biblioteca dinâmica

No desenvolvimento de projetos iOS, usamos bibliotecas estáticas e bibliotecas dinâmicas o tempo todo, sem o suporte de bibliotecas, será muito difícil. Por exemplo, se você deseja desenhar uma interface, não pode prescindir da biblioteca UIKit.As várias estruturas de dados básicas que você deseja usar, como NSString, NSArray, etc., também são inseparáveis ​​da biblioteca Foundation. Além da biblioteca oficial, também faremos download de bibliotecas de código aberto de terceiros de comunidades de código aberto, como o Github, para uso durante o desenvolvimento. Geralmente, as bibliotecas de terceiros que usamos ou as bibliotecas desenvolvidas por nós mesmos são usadas como bibliotecas estáticas, enquanto a maioria das bibliotecas fornecidas pelo sistema são bibliotecas dinâmicas, convenientes para compartilhamento de vários processos. Embora usemos bibliotecas todos os dias, você realmente entende de bibliotecas estáticas e bibliotecas dinâmicas? Qual é a estrutura da biblioteca estática e da biblioteca dinâmica? Qual é a diferença entre biblioteca estática e biblioteca dinâmica? Como eles são aplicados? Neste blog, vamos discutir essas questões.

1. Introdução

Existem muitas semelhanças entre bibliotecas estáticas e bibliotecas dinâmicas e, claro, também existem muitas diferenças.

A partir do nome do sufixo, o arquivo de biblioteca com o sufixo .a é uma biblioteca estática e o arquivo de biblioteca com o sufixo .dylib é uma biblioteca dinâmica. No desenvolvimento iOS, mais frequentemente as bibliotecas que usamos são sufixadas com .framework. O framework pode ser uma biblioteca estática ou uma biblioteca dinâmica.O próprio framework é um método de empacotamento. Sabemos que quando escrevemos código, escrevemos "código-fonte" e, para que o computador entenda esse código-fonte, precisamos de um compilador para compilar o código-fonte e compilá-lo em "código de máquina" que o computador possa entender. Um arquivo de origem .o será compilado em um arquivo binário .o, seja uma biblioteca estática ou uma biblioteca dinâmica uma coleção de arquivos .o. Não é suficiente que os desenvolvedores tenham apenas arquivos de biblioteca compostos de arquivos .o. É impossível para nós chamar facilmente os métodos na biblioteca sem arquivos de cabeçalho durante o desenvolvimento. Portanto, os arquivos de cabeçalho também são necessários para converter a biblioteca A interface fornecida na interface é exposta e, às vezes, alguns outros recursos podem ser necessários. Por exemplo, a biblioteca relacionada à página terá alguns recursos de imagem embutidos, etc. A função do framework é empacotar os arquivos da biblioteca, os arquivos de cabeçalho , e arquivos de recursos juntos, o que é conveniente Nós o usamos. A figura a seguir descreve a relação entre os arquivos de estrutura e os arquivos de biblioteca:

2. Crie uma biblioteca estática

Antes de entender melhor as bibliotecas estáticas, podemos criar uma experiência de biblioteca estática. Primeiro, use o Xcode para criar um novo projeto e selecione Framework, conforme mostrado na figura a seguir:

O modelo de projeto de estrutura criado gerará um arquivo de cabeçalho com o mesmo nome do projeto e uma pasta de recursos de recursos. Podemos criar novos arquivos de classe funcional. Por exemplo, podemos criar uma nova classe chamada MyLog e uma classe MyTool. Codifique como segue:

MyLog.h

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

@interface MyLog : NSObject

+ (void)log:(NSString *)str;

@end

MyLog.m

#import "MyLog.h"

@implementation MyLog

+ (void)log:(NSString *)str {
    NSLog(@"MyLog:%@",str);
}

@end

MyTool.h

#import <Foundation/Foundation.h>

@interface MyTool : NSObject

+ (NSInteger)add:(NSInteger)a another:(NSInteger)b;

@end

MyTool.m

#import "MyTool.h"

@implementation MyTool

+ (NSInteger)add:(NSInteger)a another:(NSInteger)b {
    return a + b;
}

@end

No arquivo de cabeçalho de biblioteca gerado por padrão, esses dois arquivos de cabeçalho de função são introduzidos da seguinte forma:

#import <Foundation/Foundation.h>

//! Project version number for MyStatic.
FOUNDATION_EXPORT double MyStaticVersionNumber;

//! Project version string for MyStatic.
FOUNDATION_EXPORT const unsigned char MyStaticVersionString[];

#import "MyLog.h"
#import "MyTool.h"

Antes de construir o frameworkwrok, podemos definir o framework a ser construído como uma biblioteca dinâmica dinâmica ou uma biblioteca estática. Primeiro construímos como uma biblioteca estática, e configuramos o Mach-o Type da opção de compilação para Static Library, como segue:

Depois disso, você pode deixar o Xcode construir e, em seguida, encontrar o arquivo de estrutura gerado na pasta Produtos correspondente, conforme mostrado na figura a seguir:

Se você observar o conteúdo do pacote deste arquivo de estrutura, descobrirá que existem 5 tipos de arquivos, conforme a seguir:

Entre eles, o arquivo de assinatura do framework é armazenado em _CodeSignature.

Os arquivos de cabeçalho são armazenados nos cabeçalhos, deve-se observar que ao compilar o projeto do framework, os arquivos de cabeçalho que precisam ser expostos devem ser definidos como públicos.

O arquivo Info.plist é o arquivo de configuração do framework atual.

O arquivo modulemap em Módulos é usado para gerenciar o mapa do módulo LLVM e definir a estrutura do componente.

Em seguida, podemos tentar usar essa biblioteca estática, usar o Xcode para criar um novo projeto iOS chamado LibDemo, arrastar o arquivo MyStatic.framework criado anteriormente para este projeto e encontrar os Caminhos de pesquisa e o cabeçalho do Framework nas opções de compilação do projeto Em Caminhos de pesquisa, configure o caminho do framework e o caminho do arquivo de cabeçalho respectivamente, conforme mostrado na figura a seguir:

Modifique o arquivo ViewController.m do projeto de teste da seguinte forma:

#import "ViewController.h"
#import "MyStatic.framework/Headers/MyStatic.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    NSInteger a = 100;
    NSInteger b = 200;
    NSInteger c = [MyTool add:a another:b];
    [MyLog log:[NSString stringWithFormat:@"%ld", c]];
}


@end

Executando o código, podemos ver no console que nossa biblioteca estática está funcionando corretamente. Você pode achar que a maneira de importar os arquivos de cabeçalho acima é muito feia, você pode criar uma nova pasta no projeto e copiar os arquivos de cabeçalho no pacote do framework, conforme mostrado abaixo:

Dessa forma, você pode usar as funções no framework como referenciar os arquivos de cabeçalho no projeto:

#import "ViewController.h"
#import "MyStatic.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    NSInteger a = 100;
    NSInteger b = 200;
    NSInteger c = [MyTool add:a another:b];
    [MyLog log:[NSString stringWithFormat:@"%ld", c]];
}

@end

3. Experimente a biblioteca dinâmica

O processo de construção e uso de bibliotecas estáticas parece muito fácil, e bibliotecas dinâmicas devem ser semelhantes. Vamos tentar agora, use o Xcode para criar um novo projeto de framework chamado MyDylib, altere o Mach-O Type nas opções de compilação para Dynamic Library e crie algumas classes de teste simples como segue:

MeuObjetoUm.h

#import <Foundation/Foundation.h>

@interface MyObjectOne : NSObject

@property(copy) NSString *name;

@end

MyObjectOne.m

#import "MyObjectOne.h"

@implementation MyObjectOne

@end

MeuObjetoDois.h

#import <Foundation/Foundation.h>

@interface MyObjectTwo : NSObject

@property(copy) NSString *title;

@end

MeuObjetoDois.m

#import "MyObjectTwo.h"

@implementation MyObjectTwo

@end

Da mesma forma, arraste o arquivo da estrutura construída para o projeto de teste, configure o caminho do arquivo de cabeçalho e adicione o código de teste da seguinte forma:

#import "ViewController.h"
#import "MyStatic.h"
#import "MyDylib.framework/Headers/MyDylib.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    NSInteger a = 100;
    NSInteger b = 200;
    NSInteger c = [MyTool add:a another:b];
    [MyLog log:[NSString stringWithFormat:@"%ld", c]];
    
    MyObjectOne *one = [[MyObjectOne alloc] init];
    one.name = @"Hello";
    [MyLog log:one.name];
}

Tente compilar e executar. Até agora, tudo parece estar normal, mas quando o programa for executado, ele travará e o console exibirá as seguintes informações:

dyld[72035]: Library not loaded: @rpath/MyDylib.framework/MyDylib

O motivo desta exceção é que o arquivo da biblioteca dinâmica não foi encontrado. Apareceu a diferença entre a biblioteca dinâmica da biblioteca estática e a biblioteca dinâmica. Como resolver este problema é realmente muito simples. Encontramos o arquivo executável de saída compilado pelo projeto de teste atual, clique para exibir o conteúdo do pacote, crie uma nova pasta Frameworks nele e copie o arquivo MyDylib.framework para ele, conforme mostrado na figura a seguir:

Agora execute o projeto novamente, você verá que o programa pode ser executado normalmente. No entanto, a operação de copiar manualmente a biblioteca dinâmica para o arquivo executável é muito deselegante.Se realmente quisermos usar a biblioteca dinâmica no projeto, usaremos mais frequentemente scripts automatizados para realizar a etapa de copiar os arquivos da biblioteca.

Por meio dessas práticas, parece que conseguimos sentir a diferença entre bibliotecas estáticas e bibliotecas dinâmicas, mas qual é a diferença? Continuamos a explorar com perguntas.

4. A diferença entre biblioteca estática e biblioteca dinâmica

Ⅰ. Como carregar

A razão pela qual a biblioteca dinâmica anterior não pode ser encontrada é, na verdade, que o método de carregamento da biblioteca dinâmica e da biblioteca estática é diferente.

Biblioteca estática: Quando a biblioteca estática estiver vinculada, ela será totalmente copiada para o arquivo executável. Se houver vários aplicativos que usam a mesma biblioteca estática, haverá um código completo da biblioteca estática no arquivo binário de cada aplicativo.

Biblioteca dinâmica: Quando o programa está vinculado, a biblioteca dinâmica não é copiada no arquivo binário, mas é carregada dinamicamente na memória pelo sistema para o programa chamar quando o programa estiver em execução. Devido a esse recurso, o sistema de biblioteca dinâmica pode ser carregado apenas uma vez e compartilhado pelo aplicativo.

Para o método de carregamento de biblioteca dinâmica de biblioteca estática, podemos dar um passo adiante. Em primeiro lugar, a biblioteca estática será completamente copiada para o arquivo executável. A completude aqui é realmente imprecisa. Quando introduzimos bibliotecas de terceiros, geralmente precisamos configurar a opção -Objc em Outros sinalizadores de vinculador do projeto. função deste item É definir a otimização do link.

Por padrão, quando a biblioteca estática está vinculada, ela não copiará todo o código para o arquivo executável, apenas copiará o código usado, o que pode reduzir o tamanho do pacote final do aplicativo, mas o dinamismo da linguagem OC determina não é necessário consultar o código diretamente, e esse método de conexão geralmente causa problemas de tempo de execução.

Após definir a opção -Objc, o vinculador carregará todas as classes OC e suas categorias correspondentes, independentemente de o código ser usado ou não.

Depois de definir a opção -all_load, o vinculador carregará todos os arquivos de objeto, não se limitando apenas aos arquivos OC.

A configuração do parâmetro -force_load pode especificar para forçar o carregamento de todos os arquivos de objeto de uma biblioteca estática.Para esta biblioteca estática, o efeito é o mesmo que -all_load.

Para bibliotecas dinâmicas, o vinculador não tem como fazer essa otimização, pois a biblioteca dinâmica é carregada em tempo de execução, e o vinculador não sabe qual código será usado. A otimização de tamanho parece ser melhor que a biblioteca dinâmica, mas é Realmente assim? Deixamos primeiro um prenúncio, para depois analisá-lo.

Ⅱ. A estrutura do arquivo é diferente

A diferença essencial entre uma biblioteca estática e uma biblioteca dinâmica é que a estrutura dos arquivos construídos é completamente diferente. Os arquivos da biblioteca podem ser visualizados usando a ferramenta MachOView.

Vamos falar primeiro sobre a biblioteca estática A estrutura da biblioteca estática aberta pelo MachOView é a seguinte:

Pode-se ver que a estrutura da biblioteca estática é realmente relativamente simples. Exceto por alguns arquivos de descrição e tabelas de símbolos da própria biblioteca, ela é basicamente uma coleção de outros arquivos executáveis. Como você pode ver na figura, cada arquivo executável terá alguns dados de cabeçalho, esses dados de cabeçalho registram o nome do executável, tamanho e outras informações. Você pode clicar em qualquer arquivo executável, que contém vários segmentos de código, segmentos de dados e outros dados com os quais estamos familiarizados:

Vejamos novamente a biblioteca dinâmica, sua estrutura é a seguinte:

Pode-se ver que a própria biblioteca dinâmica é um arquivo executável, que não é uma simples coleção de todos os arquivos .o internos, mas um arquivo de imagem que é finalmente vinculado. Como a biblioteca dinâmica está vinculada em tempo de execução, ela não pode ser otimizada em tempo de compilação, o que pode aumentar o tamanho do pacote do aplicativo, mas em aplicações práticas, usamos principalmente o parâmetro -Objc para forçar a biblioteca estática a vincular todos os arquivos OC, e Cada arquivo .o na biblioteca estática terá uma informação de cabeçalho, enquanto a biblioteca dinâmica omite esta parte da informação, portanto, no final, a biblioteca estática não é necessariamente melhor em termos de afetar o tamanho do pacote do aplicativo. Mas uma coisa é certa, as bibliotecas estáticas são vinculadas em tempo de compilação, o que economizará o tempo de inicialização do aplicativo. Muitas vezes, ao fazer projetos de otimização, não há um plano fixo, temos que escolher o plano mais adequado de acordo com a situação real.

5. Biblioteca dinâmica e tempo de execução

Ⅰ. Carregamento de bibliotecas dinâmicas

Quando se trata de tempos de execução, há muito para os desenvolvedores fazerem. Antes de tudo, vamos pensar no projeto de teste anterior: se não copiamos o arquivo de biblioteca dinâmica para o pacote IPA, por que o programa executado não consegue encontrar o arquivo de biblioteca? E por que precisamos copiar a biblioteca dinâmica para a pasta Frameworks do pacote IPA? Nenhuma outra pasta?

Para explicar o problema acima, ainda temos que olhar para o princípio de carregamento de bibliotecas dinâmicas. Você pode usar o MachOView para abrir o arquivo executável do pacote da aplicação de teste e encontrar nele a seção Carregar Comandos, conforme mostrado na figura a seguir:

Pode-se ver que existem algumas instruções de carregamento para bibliotecas dinâmicas. Foundation, UIKit, etc. são todas bibliotecas dinâmicas do sistema. Podemos ver o caminho de carregamento detalhado em seus detalhes, como segue:

Para nossa própria biblioteca MyDylib, o caminho de carregamento é o seguinte:

Pode-se ver que esta biblioteca dinâmica é carregada a partir do caminho @rpath/MyDylib.framework/MyDylib. A configuração deste caminho de carregamento foi determinada quando a biblioteca dinâmica é compilada. Podemos olhar para o projeto MyDylib, que é compilado e configurado em Xcode Options, encontre a opção Dynamic Library Install Name, conforme mostrado abaixo:

O @rpath aqui é na verdade uma variável de ambiente. Você pode configurar o valor de @rpath no projeto do aplicativo. Procure por rpath nas opções de compilação do projeto LibDemo, e você poderá ver a configuração desta variável de ambiente:

Agora sabemos que o arquivo da biblioteca dinâmica não precisa ser colocado na pasta Frameworks. Modifique o caminho da variável @rpath para modificar o caminho de carregamento da biblioteca dinâmica.

Para este método de carregamento da biblioteca dinâmica, em princípio, podemos modificar o caminho de carregamento do arquivo binário, ou substituir diretamente o arquivo da biblioteca dinâmica no pacote para obter algumas funções de injeção reversa, o que é muito legal.

Ⅱ. Biblioteca dinâmica de carregamento de código

Bibliotecas dinâmicas são carregadas em tempo de execução e também podemos usar código para controlar dinamicamente o carregamento de bibliotecas dinâmicas em tempo de execução. Você pode excluir todas as referências a MyDylib no projeto de teste e também remover o caminho do arquivo de cabeçalho configurado. Copiamos essa biblioteca dinâmica no Bundle do projeto, da seguinte forma:

O código para modificar a classe ViewController é o seguinte:

#import "ViewController.h"
#import "MyStatic.h"
#import <dlfcn.h>

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    NSInteger a = 100;
    NSInteger b = 200;
    NSInteger c = [MyTool add:a another:b];
    [MyLog log:[NSString stringWithFormat:@"%ld", c]];
    
    NSString *path = [[[NSBundle mainBundle] pathForResource:@"MyDylib" ofType:@"framework"] stringByAppendingString:@"/MyDylib"];
    // 载入动态库
    void * p = dlopen([path cStringUsingEncoding:NSUTF8StringEncoding], RTLD_LAZY);
    if (p) {
        // 加载动态库成功 直接使用
        Class cls = NSClassFromString(@"MyObjectOne");
        NSObject *obj = [[cls alloc] init];
        [obj performSelector:@selector(setName:) withObject:@"Hello"];
        [MyLog log:[obj performSelector:@selector(name)]];
    }
}

@end

Neste ponto, compile e execute o projeto novamente. Se você observar o arquivo binário do projeto de teste, não há nenhum MyDylib carregado no comando load, mas o programa ainda pode ser executado normalmente. A função da função dlopen é carregar o link dinâmico em tempo de execução.Depois que a biblioteca é carregada com sucesso, podemos chamar diretamente o código na biblioteca dinâmica com a ajuda do método de tempo de execução de OC. Dessa forma, podemos realmente realizar o download dinâmico e o uso do plug-in, de modo que o aplicativo tenha uma capacidade de atualização a quente muito alta, mas deve-se notar que a maneira de baixar dinamicamente a biblioteca dinâmica não pode ser colocar na AppStore, só podemos testar o App Ou usá-lo em aplicativos corporativos.

Indo um passo adiante, na verdade, a biblioteca dinâmica não é necessariamente lida da sandbox local. Ao depurar localmente, você pode ler o arquivo da biblioteca dinâmica de qualquer local e carregá-lo, o que pode realizar muitas funções muito interessantes localmente, como Injeção ferramenta, que monitora as alterações de arquivos de código por meio de um serviço, em seguida, empacota-o em uma biblioteca dinâmica e injeta-o no programa e, em seguida, substitui classes e métodos em tempo de execução para obter o efeito de atualização a quente de projetos iOS desenvolvidos localmente, o que é muito fácil de usar.

Foque em tecnologia, entenda o amor, esteja disposto a compartilhar, seja um amigo

QQ:316045346

{{o.name}}
{{m.name}}

Acho que você gosta

Origin my.oschina.net/u/2340880/blog/5323143
Recomendado
Clasificación