MJExtension和JSONModel的使用

来源:http://www.cnblogs.com/billios/archive/2016/10/29/6010166.html
-Advertisement-
Play Games

1、使用目的:實現JSON 與model之間的轉換。 我們經常要從伺服器上獲取JSON數據,將其轉化為Model。 這個過程是無疑是痛苦的。對於JSON數據量相對較少,或者Model裡面的屬性值較少的情況,處理起來不大費勁。但上架的應用大多是數據量巨大,與後臺交互頻繁的。更糟的是,後臺介面頻繁變化, ...


1、使用目的:實現JSON 與model之間的轉換。

我們經常要從伺服器上獲取JSON數據,將其轉化為Model。

這個過程是無疑是痛苦的。對於JSON數據量相對較少,或者Model裡面的屬性值較少的情況,處理起來不大費勁。但上架的應用大多是數據量巨大,與後臺交互頻繁的。更糟的是,後臺介面頻繁變化,那麼維護起來就相當費勁了,因為你每次都要根據新的介面文檔來逐一解釋數據。往往每次要花你半天時間去修改、調試代碼。

 

2、JSONModel

JSON -> Dictionary -> Model

 

以下麵的JSON文件為例:

{
    "data" : [
                    {
                        "name" : "張三",
                        "gender" : "male"
                    },
                    {
                        "name" : "李四",
                        "gender" : "female"
                    },
                    {
                        "name" : "黃五",
                        "gender" : "male"
                    }
                ]
}

 

上述JSON分析:

Data 就是 數組。數組裡每一個元素也是一個字典。

假設我們tableView里每個cell都要展示一個名字和性別,那麼我們需要把一組名字和性別做成一個model來使用。

 

所以在邏輯上,這個json就是兩類mode

1

 

{
    "name" : "張三",
    "gender" : "male"
  }

 

2、

{

    "data" : [
                    {
                        "name" : "張三",
                        "gender" : "male"
                    },
                    {
                        "name" : "李四",
                        "gender" : "female"
                    },
                    {
                        "name" : "黃五",
                        "gender" : "male"
                    }
                ]
}

 

一般來說,一個字典就是一個model

所以,我們要創建的就是這兩個model類。

 

Model.h文件------------------------------------

 

#import <JSONModel/JSONModel.h>

@protocol OneModel //<NSObject>

@end

@interface OneModel : JSONModel

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


@end

@interface Model : JSONModel

@property (strong, nonatomic) NSArray<OneModel> *data;

@end

 

 

Model.m文件------------------------------------

#import "Model.h"

@implementation OneModel

@end

@implementation Model

@end

 

 

註意兩點:

1、需要做成多少個model,就該有多少個@interface@implementation

2、Model裡面的屬性名必須與json裡面的一樣。

 

TableViewController.h

 

 

#import <UIKit/UIKit.h>
//@class Model;
@class MyMJModel;
@interface TableViewController : UITableViewController

//@property(nonatomic,strong) Model *model;
@property(nonatomic,strong) MyMJModel *model;

@end

 

TableViewController.m

 

#import "TableViewController.h"
#import "Model.h"
#import "MyMJModel.h"
#import "TableViewCell.h"
#import "Status.h"
#import <MJExtension/MJExtension.h>
@interface TableViewController ()

@end

@implementation TableViewController
static NSString *reuseID = @"reuse";
- (void)viewDidLoad {
    [super viewDidLoad];
    
    [self.tableView registerClass:[TableViewCell class] forCellReuseIdentifier:reuseID];
    
    
    [self initData];
}

#pragma mark - 獲取JSON數據
- (void)initData {
    NSString *path = [[NSBundle mainBundle]pathForResource:@"Model" ofType:@"json"];
    NSData *data = [NSData dataWithContentsOfFile:path];
    if (data) {
        NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:nil];

//        self.model = [[Model alloc]initWithDictionary:dict error:nil];

        [MyMJModel mj_setupObjectClassInArray:^NSDictionary *{
            return @{
                     @"users" : @"User"
                     };
        }];
        
        self.model = [MyMJModel mj_objectWithKeyValues:dict];
        
    }
    
    [self objectArray2keyValuesArray];
    
}

#pragma mark - 隱藏狀態欄
- (BOOL)prefersStatusBarHidden{
    return YES;
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

#pragma mark - Table view data source

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    return 1;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    
    return self.model.users.count;
}


- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    TableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:reuseID forIndexPath:indexPath];
    
    cell.oneModel = self.model.users[indexPath.row];
    return cell;
}

/**
 *  簡單的字典 -> 模型
 */
- (void) keyValues2object
{
    // 1.定義一個字典
    NSDictionary *dict = @{
                           @"name" : @"Jack",
                           @"gender" : @"male",
                           };
    
    // 2.將字典轉為User模型
    User *user = [User mj_objectWithKeyValues:dict];
    
    // 3.列印User模型的屬性
    NSLog(@"name = %@, gender = %@", user.name, user.gender);
}

/**
 *  複雜的字典 -> 模型 (模型裡面包含了模型)
 */
- (void) keyValues2object2
{
    // 1.定義一個字典
    NSDictionary *dict = @{
                           @"text" : @"是啊,今天天氣確實不錯!",
                           
                           @"user" : @{
                                   @"name" : @"Jack",
                                   @"gender" : @"male"
                                   }
                           };
    
    // 2.將字典轉為Status模型
    Status *status = [Status mj_objectWithKeyValues:dict];
    
    // 3.列印status的屬性
    NSString *text = status.text;
    NSString *name = status.user.name;
    NSString *gender = status.user.gender;
    NSLog(@"text=%@, name=%@, gender=%@", text, name, gender);
}

/**
 *  字典數組 -> 模型數組
 */
- (void) keyValuesArray2objectArray
{
    // 1.定義一個字典數組
    NSArray *dictArray = @[
                           @{
                               @"name" : @"Jack",
                               @"gender" : @"male",
                               },
                           
                           @{
                               @"name" : @"Rose",
                               @"gender" : @"female",
                               }
                           ];
    
    // 2.將字典數組轉為User模型數組
    NSArray *userArray = [User mj_objectArrayWithKeyValuesArray:dictArray];
    
    // 3.列印userArray數組中的User模型屬性
    for (User *user in userArray) {
        NSLog(@"name=%@, gender=%@", user.name, user.gender);
    }
}

/**
 *  模型 -> 字典
 */
- (void) object2keyValues
{
    // 1.新建模型
    User *user = [[User alloc] init];
    user.name = @"Jack";
    user.gender = @"lufy.png";
    
    Status *status = [[Status alloc] init];
    status.user = user;
    status.text = @"今天的心情不錯!";
    
    // 2.將模型轉為字典
    //    NSDictionary *dict = [status keyValues];
    NSDictionary *dict = status.mj_keyValues;
    NSLog(@"%@", dict);
}

/**
 *  模型數組 -> 字典數組
 */
-(void) objectArray2keyValuesArray
{
    // 1.新建模型數組
    User *user1 = [[User alloc] init];
    user1.name = @"Jack";
    user1.gender = @"male";
    
    User *user2 = [[User alloc] init];
    user2.name = @"Rose";
    user2.gender = @"female";
    
    NSArray *userArray = @[user1, user2];
    
    // 2.將模型數組轉為字典數組
    NSArray *dictArray = [User mj_keyValuesArrayWithObjectArray:userArray];
    NSLog(@"%@", dictArray);
}

@end

 

TableViewCell.h

 

#import <UIKit/UIKit.h>
//@class OneModel;
@class User;
@interface TableViewCell : UITableViewCell

//@property (strong,nonatomic) OneModel *oneModel;
@property (strong,nonatomic) User *oneModel;

@end

 

TableViewCell.m

 

#import "TableViewCell.h"
#import "Model.h"
#import "MyMJModel.h"

@implementation TableViewCell

- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier{
    
    self = [super initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:reuseIdentifier];
    
    if (self) {
        
    }
    
    return self;
}

- (void)awakeFromNib {
    [super awakeFromNib];
    // Initialization code
}

- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
    [super setSelected:selected animated:animated];
    
    // Configure the view for the selected state
}

//- (void)setOneModel:(OneModel *)oneModel {
//    _oneModel = oneModel;
//    self.textLabel.text = oneModel.name;
//    self.detailTextLabel.text = oneModel.gender;
//}
- (void)setOneModel:(User *)oneModel {
    _oneModel = oneModel;
    
    self.textLabel.text = oneModel.name;
    self.detailTextLabel.text = oneModel.gender;
}

@end

 

Model.h

 

#import <JSONModel/JSONModel.h>

@protocol OneModel //<NSObject>

@end

@interface OneModel : JSONModel

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


@end

@interface Model : JSONModel

@property (strong, nonatomic) NSArray<OneModel> *data;

@end

 

Model.m

 

 

#import <JSONModel/JSONModel.h>

@protocol OneModel //<NSObject>

@end

@interface OneModel : JSONModel

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


@end

@interface Model : JSONModel

@property (strong, nonatomic) NSArray<OneModel> *data;

@end

 

 

 

步驟總結:

步驟一:通過CocoaPod安裝JSONModel。(不再贅述)

步驟二:分析JSON數據和業務需求,搭建UITableViewCell、UITableViewController等代碼。(不再贅述)

步驟三:根據分析JSON數據,Model文件,只需寫.h文件,.m文件對應類的個數寫@implementation@end,其他不用寫。

 

3MJExtention

MJExtension是一套字典和模型之間互相轉換的超輕量級框架

JSON --> Model、Core Data Model

JSONString --> Model、Core Data Model

Model、Core Data Model --> JSON

JSON Array --> Model Array、Core Data Model Array

JSONString --> Model Array、Core Data Model Array

Model Array、Core Data Model Array --> JSON Array

 

 

 

 

 

 

對比之前JSONModel的例子,我們用MJExtention來實現:

 

 

 

 

 

MJExtensionruntime原理:

.runtime介紹

  runtime翻譯就是運行時,我們稱為運行時機制.在OC中最重要的體現就是消息發送機制.

  1)在C語言中,程式在編譯過程中就決定調用哪個函數.

  2)在OC中,編譯的時候不會決定調用哪個函數,只要聲明瞭這個函數即可.只有在真正運行的時候,才會去決定調用哪個函數.

.runtime用法,總結了下大概有以下幾種用法.

1>發送消息

  1)OC調用方法本質就是發送消息,要用消息機制,需要導入<objc/message.h>才可以使用.

  2)objc_msgSend,是只有對象才能發送消息,只能以objc開頭.

 

    // 創建person對象

    Person *p = [[Person alloc] init];

 

    // 調用對象方法    [p read];

 

    // 本質:讓對象發送消息    objc_msgSend(p, @selector(read));

 

    // 調用類方法的方式:兩種

    // 第一種通過類名調用    [Person read];

    // 第二種通過類對象調用

    [[Person class] read];

 

    // 用類名調用類方法,底層會自動把類名轉換成類對象調用

    // 本質:讓類對象發送消息

    objc_msgSend([Person class], @selector(read));

下麵我畫了個消息機制的原理圖

 

 

 

2>交換方法

  個人覺得有點類似於分類或者是類擴展,但是也有區別,它可以保證在系統原有的方法基礎上加一些其他方法

@implementation UIImage (Image)// 載入分類到記憶體的時候調用

+ (void)load
{
    // 交換方法
    // 獲取imageWithName方法地址
    Method imageWithName = class_getClassMethod(self, @selector(imageWithName:));

    // 獲取imageWithName方法地址
    Method imageName = class_getClassMethod(self, @selector(imageNamed:));

    // 交換方法地址,相當於交換實現方式    
  method_exchangeImplementations(imageWithName, imageName);
}

// 不能在分類中重寫系統方法imageNamed,因為會把系統的功能給覆蓋掉,而且分類中不能調用super.
// 既能載入圖片又能列印

+ (instancetype)imageWithName:(NSString *)name
{
    // 這裡調用imageWithName,相當於調用imageName
    UIImage *image = [self imageWithName:name];

    if (image == nil) {
        NSLog(@"載入空的圖片");
    }
    return image;

}

@end

 

交換方法的原理圖片如下

 

 

 

3>動態添加方法(performSelector)

如果一個類有很多方法,載入到記憶體中生成方法列表需要消耗很多記憶體,使用動態添加方法可以節省記憶體.

@implementation ViewController

- (void)viewDidLoad {

    [super viewDidLoad];

    // Do any additional setup after loading the view, typically from a nib.
    Person *p = [[Person alloc] init];


    // 預設person,沒有實現eat方法,可以通過performSelector調用,但是會報錯。
    // 動態添加方法就不會報錯    [p performSelector:@selector(eat)];
}
@end

@implementation Person// void(*)()// 預設方法都有兩個隱式參數,void eat(id self,SEL sel)
{
    NSLog(@"%@ %@",self,NSStringFromSelector(sel));
}

// 當一個對象調用未實現的方法,會調用這個方法處理,並且會把對應的方法列表傳過來.
+ (BOOL)resolveInstanceMethod:(SEL)sel
{
    if (sel == @selector(eat)) {
        // 動態添加eat方法
        class_addMethod(self, @selector(eat), eat, "v@:");

    }
    return [super resolveInstanceMethod:sel];
}

@end

 

4>動態添加屬性

原理就是給一個類聲明屬性,就是給一個類添加關聯,而不是把屬性的記憶體添加到這個類的記憶體.

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    // Do any additional setup after loading the view, typically from a nib.
    // 給系統NSObject類動態添加屬性name
    NSObject *objc = [[NSObject alloc] init];

    objc.name = @"cjh";

    NSLog(@"%@",objc.name);
}

@end

// 定義關聯的keystatic const char *key = "name";

@implementation NSObject (Property)
- (NSString *)name
{
    // 根據關聯的key,獲取關聯的值。
    return objc_getAssociatedObject(self, key);
}

- (void)setName:(NSString *)name
{
      objc_setAssociatedObject(self,key,name,OBJC_ASSOCIATION_RETAIN_NONATOMIC);      
}

@end

 

5>字典轉模型

MJExtension框架我們應該不陌生,裡面字典轉模型就是利用了runtime來實現的.

  1)首先,模型設計上,屬性我們通常是根據字典來設計的,但是每次都一個一個來寫的話很麻煩,我們可以設計一個分類,根據字典生成一個對應的字元串,就是我們想要的模型設計屬性.

 

@implementation NSObject (Log)

// 自動列印屬性字元串
+ (void)resolveDict:(NSDictionary *)dict{

    // 拼接屬性字元串代碼
    NSMutableString *strM = [NSMutableString string];

    // 1.遍歷字典,把字典中的所有key取出來,生成對應的屬性代碼
    [dict enumerateKeysAndObjectsUsingBlock:^(id  _Nonnull key, id  _Nonnull obj, BOOL * _Nonnull stop) {

         NSString *type;

        if ([obj isKindOfClass:NSClassFromString(@"__NSCFString")]) {

            type = @"NSString";

        }else if ([obj isKindOfClass:NSClassFromString(@"__NSCFArray")]){

            type = @"NSArray";

        }else if ([obj isKindOfClass:NSClassFromString(@"__NSCFNumber")]){

            type = @"int";

        }else if ([obj isKindOfClass:NSClassFromString(@"__NSCFDictionary")]){

            type = @"NSDictionary";

        }

        // 屬性字元串
        NSString *str;
        if ([type containsString:@"NS"]) {

            str = [NSString stringWithFormat:@"@property (nonatomic, strong) %@ *%@;",type,key];

        }else{

            str = [NSString stringWithFormat:@"@property (nonatomic, assign) %@ %@;",type,key];

        }

        // 每生成屬性字元串,就自動換行。
        [strM appendFormat:@"\n%@\n",str];
    }];

    // 把拼接好的字元串列印出來,就好了。
    NSLog(@"%@",strM);

}
@end

 

2)利用runtime賦值,註意一下的區別.

 KVC: 遍歷字典中所有key,去模型中查找

runtime: 遍歷模型中所有屬性,去字典中查找對應value,然後在賦值

@implementation NSObject (Model)

+ (instancetype)modelWithDict:(NSDictionary *)dict
{
    // 思路:遍歷模型中所有屬性-》使用運行時

    // 0.創建對應的對象
    id objc = [[self alloc] init];

    // 1.利用runtime給對象中的成員屬性賦值
    unsigned int count;

    // 獲取類中的所有成員屬性(使用copy,不影響內部的ivar)
    Ivar *ivarList = class_copyIvarList(self, &count);


    for (int i = 0; i < count; i++) {
        // 根據角標,從數組取出對應的成員屬性
        Ivar ivar = ivarList[i];

        // 獲取成員屬性名
        NSString *name = [NSString stringWithUTF8String:ivar_getName(ivar)];

        // 處理成員屬性名->字典中的key
        // 從第一個角標開始截取
        NSString *key = [name substringFromIndex:1];

        // 根據成員屬性名去字典中查找對應的value
        id value = dict[key];

        // 二級轉換:如果字典中還有字典,也需要把對應的字典轉換成模型
        // 判斷下value是否是字典
        if ([value isKindOfClass:[NSDictionary class]]) {

            // 獲取成員屬性類型
           NSString *type = [NSString stringWithUTF8String:ivar_getTypeEncoding(ivar)];

            // 裁剪類型字元串
            NSRange range = [type rangeOfString:@"\""];

           type = [type substringFromIndex:range.location + range.length];

            range = [type rangeOfString:@"\""];

            // 裁剪到哪個角標,不包括當前角標
          type = [type substringToIndex:range.location];

            // 根據字元串類名生成類對象
            Class modelClass = NSClassFromString(type);

            if (modelClass) { // 有對應的模型才需要轉
                // 把字典轉模型
                value  =  [modelClass modelWithDict:value];
            }
        }

        // 三級轉換:NSArray中也是字典,把數組中的字典轉換成模型.
        // 判斷值是否是數組
        if ([value isKindOfClass:[NSArray class]]) {
            // 判斷對應類有沒有實現字典數組轉模型數組的協議
            if ([self respondsToSelector:@selector(arrayContainModelClass)]) {

                // 轉換成id類型,就能調用任何對象的方法
                id idSelf = self;

                // 獲取數組中字典對應的模型
                NSString *type =  [idSelf arrayContainModelClass][key];

                // 生成模型
               Class classModel = NSClassFromString(type);

                NSMutableArray *arrM = [NSMutableArray array];

                // 遍歷字典數組,生成模型數組
                for (NSDictionary *dict in value) {
                    // 字典轉模型
                  id model =  [classModel modelWithDict:dict];
                    [arrM addObject:model];
                }

                // 把模型數組賦值給value
                value = arrM;

            }
        }

        if (value) { // 有值,才需要給模型的屬性賦值

            // 利用KVC給模型中的屬性賦值            
            [objc setValue:value forKey:key];

        }
    }
    return objc;
}


@end    

 

4MJExtentionJSONModel的對比:

1jsonmodel之間的轉換效率對比:

 

 

運行效率,MJExtensionJSONModel20倍。

MJExtension > JSONModel > Mantle

 

2、使用複雜度對比:

對於複雜的字典,JSONModel處理的原理是通過協議名,去找到名字與之一樣的類,而裝成一個model的。所以JSONModelMJExtention要多寫一個代理,即便這個代理是空的。

MJExtention要在使用的時候說明數組裡每個元素的類是什麼類。

 

所以,在使用複雜度上,各有千秋。

 

3、耦合度對比:

MJExtention用的是類別category,而JSONModel在寫model類時,則必須繼承自JSONModel

所以在耦合度的對比上,MJExtention占優!

 

5、特殊情況處理:

1Objective-C里的關鍵字,例如,id

MJExtensionidObjective-C里的關鍵字,我們一般用大寫的ID替換,但是往往伺服器給我們的數據是小寫的id,這個時候就可以用MJExtension框架里的方法轉換一下:

+ (NSDictionary *)mj_replacedKeyFromPropertyName  {

    return @{@"ID": @"id"};
}

 

JSONModel

在你的model的.m(實現)文件中:

+ (JSONKeyMapper *)keyMapper  {

    return [[JSONKeyMapper alloc] initWithDictionary:@{@"description" : @"bank_description", @"id" : @"bank_id"}];

}

 

本文的代碼可下載下麵文件:

示例代碼


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 通過jquery可以很容易實現CP端的拖拽。但是在移動端卻不好用了。於是我自己寫了一個在移動端的拖拽demo,主要用到的事件是觸摸事件(touchstart,touchmove和touchend)。 這個demo實現的功能是:可以拖拽的元素(在這裡是圖片)位於列表中,這些元素可以被拖到指定區域,到達 ...
  • 1.水平居中設置 行內元素居中設置:如果被設置元素為文本、圖片等行內元素時,水平居中是通過給父元素設置 text-align:center 來實現的。代碼示例如下: 定寬塊狀元素居中設置:滿足定寬和塊狀兩個條件的元素是可以通過設置“左右margin”值為“auto”來實現居中的。代碼示例如下: 不定 ...
  • 1. 合理使用title, description, keywords2. 合理使用h1 - h6, h1標簽的權重很高, 註意使用頻率3. 列表代碼使用ul, 重要文字使用strong標簽4. 圖片添加alt屬性, title可選。 img最好加上寬, 高利於載入5. 重要代碼在前面, 通過css ...
  • 使用jQuery動畫時,當快速操作時,讓動畫效果與滑鼠的動作保持一致 ...
  • META相關 1. 添加到主屏後的標題(IOS) 2. 啟用 WebApp 全屏模式(IOS) 當網站添加到主屏幕後再點擊進行啟動時,可隱藏地址欄(從瀏覽器跳轉或輸入鏈接進入並沒有此效果) 3. 百度禁止轉碼 通過百度手機打開網頁時,百度可能會對你的網頁進行轉碼,往你頁面貼上它的廣告,非常之噁心。不 ...
  • if ( Object.prototype.toString.call(d) "[object Date]" ) { // it is a date if ( isNaN( d.getTime() ) ) { // d.valueOf() could also work // date is not ...
  • 如果您的 iPhone 應用里有個 view,既有單擊操作又有雙擊操作。用戶雙擊 view 時,總是先執行一遍單擊的操作再執行雙擊的操作。所以直接判斷時就會發現不能直接進入雙擊操作。下麵是區分 touch 事件是單擊還是雙擊的方法 -(void)singleTap{ NSLog(@"Tap 1 ti ...
  • 做項目的時候經常會用到標簽,比如說現在很多項目中搜索歷史用標簽展示 和 選擇某個產品的不同屬性用標簽展示...。網上的有很多封裝好的標簽,但是作為一個上進的程式員,都希望能有一個自己寫的。其實也是一種積累知識的過程。現在的這個標簽是根據你的標簽的搜字母來排序的。先來看下效果: 下麵的是代碼說明和代碼 ...
一周排行
    -Advertisement-
    Play Games
  • 示例項目結構 在 Visual Studio 中創建一個 WinForms 應用程式後,項目結構如下所示: MyWinFormsApp/ │ ├───Properties/ │ └───Settings.settings │ ├───bin/ │ ├───Debug/ │ └───Release/ ...
  • [STAThread] 特性用於需要與 COM 組件交互的應用程式,尤其是依賴單線程模型(如 Windows Forms 應用程式)的組件。在 STA 模式下,線程擁有自己的消息迴圈,這對於處理用戶界面和某些 COM 組件是必要的。 [STAThread] static void Main(stri ...
  • 在WinForm中使用全局異常捕獲處理 在WinForm應用程式中,全局異常捕獲是確保程式穩定性的關鍵。通過在Program類的Main方法中設置全局異常處理,可以有效地捕獲並處理未預見的異常,從而避免程式崩潰。 註冊全局異常事件 [STAThread] static void Main() { / ...
  • 前言 給大家推薦一款開源的 Winform 控制項庫,可以幫助我們開發更加美觀、漂亮的 WinForm 界面。 項目介紹 SunnyUI.NET 是一個基於 .NET Framework 4.0+、.NET 6、.NET 7 和 .NET 8 的 WinForm 開源控制項庫,同時也提供了工具類庫、擴展 ...
  • 說明 該文章是屬於OverallAuth2.0系列文章,每周更新一篇該系列文章(從0到1完成系統開發)。 該系統文章,我會儘量說的非常詳細,做到不管新手、老手都能看懂。 說明:OverallAuth2.0 是一個簡單、易懂、功能強大的許可權+可視化流程管理系統。 有興趣的朋友,請關註我吧(*^▽^*) ...
  • 一、下載安裝 1.下載git 必須先下載並安裝git,再TortoiseGit下載安裝 git安裝參考教程:https://blog.csdn.net/mukes/article/details/115693833 2.TortoiseGit下載與安裝 TortoiseGit,Git客戶端,32/6 ...
  • 前言 在項目開發過程中,理解數據結構和演算法如同掌握蓋房子的秘訣。演算法不僅能幫助我們編寫高效、優質的代碼,還能解決項目中遇到的各種難題。 給大家推薦一個支持C#的開源免費、新手友好的數據結構與演算法入門教程:Hello演算法。 項目介紹 《Hello Algo》是一本開源免費、新手友好的數據結構與演算法入門 ...
  • 1.生成單個Proto.bat內容 @rem Copyright 2016, Google Inc. @rem All rights reserved. @rem @rem Redistribution and use in source and binary forms, with or with ...
  • 一:背景 1. 講故事 前段時間有位朋友找到我,說他的窗體程式在客戶這邊出現了卡死,讓我幫忙看下怎麼回事?dump也生成了,既然有dump了那就上 windbg 分析吧。 二:WinDbg 分析 1. 為什麼會卡死 窗體程式的卡死,入口門檻很低,後續往下分析就不一定了,不管怎麼說先用 !clrsta ...
  • 前言 人工智慧時代,人臉識別技術已成為安全驗證、身份識別和用戶交互的關鍵工具。 給大家推薦一款.NET 開源提供了強大的人臉識別 API,工具不僅易於集成,還具備高效處理能力。 本文將介紹一款如何利用這些API,為我們的項目添加智能識別的亮點。 項目介紹 GitHub 上擁有 1.2k 星標的 C# ...